omnizip 0.3.1

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 (511) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/.rubocop_todo.yml +754 -0
  5. data/COPYING +502 -0
  6. data/Gemfile +17 -0
  7. data/LICENSE +12 -0
  8. data/README.adoc +1045 -0
  9. data/Rakefile +12 -0
  10. data/benchmark/README.md +260 -0
  11. data/benchmark/benchmark_suite.rb +125 -0
  12. data/benchmark/compression_bench.rb +181 -0
  13. data/benchmark/filter_bench.rb +180 -0
  14. data/benchmark/models/benchmark_result.rb +59 -0
  15. data/benchmark/models/comparison_result.rb +69 -0
  16. data/benchmark/profile_suite.rb +167 -0
  17. data/benchmark/reporter.rb +150 -0
  18. data/benchmark/run_benchmarks.rb +66 -0
  19. data/benchmark/test_data.rb +137 -0
  20. data/config/formats/rar3_spec.yml +91 -0
  21. data/config/formats/rar5_spec.yml +102 -0
  22. data/docs/.github/workflows/docs.yml +142 -0
  23. data/docs/.gitignore +21 -0
  24. data/docs/.lychee.toml +67 -0
  25. data/docs/Gemfile +13 -0
  26. data/docs/RAR_WRITE_SUPPORT.md +26 -0
  27. data/docs/README.md +101 -0
  28. data/docs/_config.yml +112 -0
  29. data/docs/assets/logo.svg +1 -0
  30. data/docs/assets/omnizip-logo.pdf +1540 -11
  31. data/docs/comparison/feature-matrix.adoc +694 -0
  32. data/docs/comparison/index.adoc +113 -0
  33. data/docs/comparison/vs-7zip.adoc +309 -0
  34. data/docs/comparison/vs-peazip.adoc +77 -0
  35. data/docs/comparison/vs-rubyzip.adoc +342 -0
  36. data/docs/comparison/vs-winrar.adoc +100 -0
  37. data/docs/compatibility.adoc +579 -0
  38. data/docs/concepts/index.adoc +129 -0
  39. data/docs/developer/architecture.adoc +256 -0
  40. data/docs/developer/contributing.adoc +158 -0
  41. data/docs/developer/index.adoc +25 -0
  42. data/docs/developer/testing.adoc +212 -0
  43. data/docs/getting-started/basic-usage.adoc +271 -0
  44. data/docs/getting-started/index.adoc +42 -0
  45. data/docs/getting-started/installation.adoc +138 -0
  46. data/docs/getting-started/quick-start.adoc +185 -0
  47. data/docs/getting-started/your-first-archive.adoc +218 -0
  48. data/docs/guides/advanced-features/encryption.adoc +300 -0
  49. data/docs/guides/advanced-features/index.adoc +49 -0
  50. data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
  51. data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
  52. data/docs/guides/advanced-features/streaming.adoc +212 -0
  53. data/docs/guides/archive-formats/gzip-format.adoc +107 -0
  54. data/docs/guides/archive-formats/index.adoc +130 -0
  55. data/docs/guides/archive-formats/rar-format.adoc +104 -0
  56. data/docs/guides/archive-formats/rar5.adoc +521 -0
  57. data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
  58. data/docs/guides/archive-formats/tar-format.adoc +106 -0
  59. data/docs/guides/archive-formats/xz-format.adoc +118 -0
  60. data/docs/guides/archive-formats/zip-format.adoc +35 -0
  61. data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
  62. data/docs/guides/compression-algorithms/deflate.adoc +319 -0
  63. data/docs/guides/compression-algorithms/index.adoc +190 -0
  64. data/docs/guides/compression-algorithms/lzma.adoc +398 -0
  65. data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
  66. data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
  67. data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
  68. data/docs/guides/creating-archives.adoc +354 -0
  69. data/docs/guides/extracting-archives.adoc +53 -0
  70. data/docs/guides/format-conversion.adoc +64 -0
  71. data/docs/guides/index.adoc +49 -0
  72. data/docs/guides/migration-rubyzip.adoc +217 -0
  73. data/docs/guides/parity-archives.adoc +605 -0
  74. data/docs/guides/performance-tuning.adoc +88 -0
  75. data/docs/index.adoc +218 -0
  76. data/docs/lychee.toml +67 -0
  77. data/docs/reference/api/overview.adoc +188 -0
  78. data/docs/reference/cli/compress-command.adoc +114 -0
  79. data/docs/reference/cli/overview.adoc +140 -0
  80. data/docs/reference/index.adoc +26 -0
  81. data/docs/resources/faq.adoc +185 -0
  82. data/docs/resources/quick-reference.adoc +222 -0
  83. data/docs/troubleshooting/index.adoc +208 -0
  84. data/examples/api_comparison.rb +205 -0
  85. data/examples/deflate64_example.rb +96 -0
  86. data/examples/par2_demo.rb +121 -0
  87. data/examples/quick_start_native.rb +150 -0
  88. data/examples/quick_start_rubyzip.rb +115 -0
  89. data/examples/rubyzip_compatibility_demo.rb +194 -0
  90. data/exe/omnizip +27 -0
  91. data/lib/omnizip/algorithm.rb +130 -0
  92. data/lib/omnizip/algorithm_registry.rb +86 -0
  93. data/lib/omnizip/algorithms/.keep +0 -0
  94. data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
  95. data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
  96. data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
  97. data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
  98. data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
  99. data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
  100. data/lib/omnizip/algorithms/bzip2.rb +130 -0
  101. data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
  102. data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
  103. data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
  104. data/lib/omnizip/algorithms/deflate.rb +128 -0
  105. data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
  106. data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
  107. data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
  108. data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
  109. data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
  110. data/lib/omnizip/algorithms/deflate64.rb +109 -0
  111. data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
  112. data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
  113. data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
  114. data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
  115. data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
  116. data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
  117. data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
  118. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
  119. data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
  120. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
  121. data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
  122. data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
  123. data/lib/omnizip/algorithms/lzma/match.rb +32 -0
  124. data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
  125. data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
  126. data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
  127. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
  128. data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
  129. data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
  130. data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
  131. data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
  132. data/lib/omnizip/algorithms/lzma/state.rb +127 -0
  133. data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
  134. data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
  135. data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
  136. data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
  137. data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
  138. data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
  139. data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
  140. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
  141. data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
  142. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
  143. data/lib/omnizip/algorithms/lzma.rb +238 -0
  144. data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
  145. data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
  146. data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
  147. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
  148. data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
  149. data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
  150. data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
  151. data/lib/omnizip/algorithms/lzma2.rb +141 -0
  152. data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
  153. data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
  154. data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
  155. data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
  156. data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
  157. data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
  158. data/lib/omnizip/algorithms/ppmd7.rb +116 -0
  159. data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
  160. data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
  161. data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
  162. data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
  163. data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
  164. data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
  165. data/lib/omnizip/algorithms/ppmd8.rb +82 -0
  166. data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
  167. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
  168. data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
  169. data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
  170. data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
  171. data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
  172. data/lib/omnizip/algorithms/zstandard.rb +138 -0
  173. data/lib/omnizip/buffer/memory_archive.rb +251 -0
  174. data/lib/omnizip/buffer/memory_extractor.rb +224 -0
  175. data/lib/omnizip/buffer.rb +176 -0
  176. data/lib/omnizip/checksum_registry.rb +114 -0
  177. data/lib/omnizip/checksums/crc32.rb +100 -0
  178. data/lib/omnizip/checksums/crc64.rb +101 -0
  179. data/lib/omnizip/checksums/crc_base.rb +158 -0
  180. data/lib/omnizip/checksums/verifier.rb +131 -0
  181. data/lib/omnizip/chunked/memory_manager.rb +194 -0
  182. data/lib/omnizip/chunked/reader.rb +78 -0
  183. data/lib/omnizip/chunked/writer.rb +120 -0
  184. data/lib/omnizip/chunked.rb +129 -0
  185. data/lib/omnizip/cli/output_formatter.rb +104 -0
  186. data/lib/omnizip/cli.rb +572 -0
  187. data/lib/omnizip/commands/.keep +0 -0
  188. data/lib/omnizip/commands/archive_create_command.rb +427 -0
  189. data/lib/omnizip/commands/archive_extract_command.rb +272 -0
  190. data/lib/omnizip/commands/archive_list_command.rb +218 -0
  191. data/lib/omnizip/commands/archive_repair_command.rb +131 -0
  192. data/lib/omnizip/commands/archive_verify_command.rb +117 -0
  193. data/lib/omnizip/commands/compress_command.rb +117 -0
  194. data/lib/omnizip/commands/decompress_command.rb +120 -0
  195. data/lib/omnizip/commands/list_command.rb +53 -0
  196. data/lib/omnizip/commands/metadata_command.rb +153 -0
  197. data/lib/omnizip/commands/parity_create_command.rb +122 -0
  198. data/lib/omnizip/commands/parity_repair_command.rb +122 -0
  199. data/lib/omnizip/commands/parity_verify_command.rb +124 -0
  200. data/lib/omnizip/commands/profile_list_command.rb +56 -0
  201. data/lib/omnizip/commands/profile_show_command.rb +44 -0
  202. data/lib/omnizip/convenience.rb +359 -0
  203. data/lib/omnizip/converter/conversion_registry.rb +49 -0
  204. data/lib/omnizip/converter/conversion_strategy.rb +121 -0
  205. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
  206. data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
  207. data/lib/omnizip/converter.rb +105 -0
  208. data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
  209. data/lib/omnizip/crypto/aes256/constants.rb +28 -0
  210. data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
  211. data/lib/omnizip/crypto/aes256.rb +102 -0
  212. data/lib/omnizip/error.rb +106 -0
  213. data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
  214. data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
  215. data/lib/omnizip/eta/rate_calculator.rb +104 -0
  216. data/lib/omnizip/eta/sample_history.rb +143 -0
  217. data/lib/omnizip/eta/time_estimator.rb +106 -0
  218. data/lib/omnizip/eta.rb +63 -0
  219. data/lib/omnizip/extraction/filter_chain.rb +177 -0
  220. data/lib/omnizip/extraction/glob_pattern.rb +140 -0
  221. data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
  222. data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
  223. data/lib/omnizip/extraction/regex_pattern.rb +50 -0
  224. data/lib/omnizip/extraction/selective_extractor.rb +240 -0
  225. data/lib/omnizip/extraction.rb +111 -0
  226. data/lib/omnizip/file_type/mime_classifier.rb +144 -0
  227. data/lib/omnizip/file_type.rb +113 -0
  228. data/lib/omnizip/filter.rb +139 -0
  229. data/lib/omnizip/filter_pipeline.rb +108 -0
  230. data/lib/omnizip/filter_registry.rb +166 -0
  231. data/lib/omnizip/filters/bcj.rb +279 -0
  232. data/lib/omnizip/filters/bcj2/constants.rb +53 -0
  233. data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
  234. data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
  235. data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
  236. data/lib/omnizip/filters/bcj2.rb +99 -0
  237. data/lib/omnizip/filters/bcj_arm.rb +176 -0
  238. data/lib/omnizip/filters/bcj_arm64.rb +244 -0
  239. data/lib/omnizip/filters/bcj_ia64.rb +196 -0
  240. data/lib/omnizip/filters/bcj_ppc.rb +190 -0
  241. data/lib/omnizip/filters/bcj_sparc.rb +176 -0
  242. data/lib/omnizip/filters/bcj_x86.rb +193 -0
  243. data/lib/omnizip/filters/delta.rb +196 -0
  244. data/lib/omnizip/filters/filter_base.rb +72 -0
  245. data/lib/omnizip/filters/registry.rb +123 -0
  246. data/lib/omnizip/filters/xz_delta.rb +258 -0
  247. data/lib/omnizip/format_detector.rb +162 -0
  248. data/lib/omnizip/format_registry.rb +59 -0
  249. data/lib/omnizip/formats/.keep +0 -0
  250. data/lib/omnizip/formats/bzip2_file.rb +172 -0
  251. data/lib/omnizip/formats/cpio/constants.rb +55 -0
  252. data/lib/omnizip/formats/cpio/entry.rb +385 -0
  253. data/lib/omnizip/formats/cpio/reader.rb +196 -0
  254. data/lib/omnizip/formats/cpio/writer.rb +234 -0
  255. data/lib/omnizip/formats/cpio.rb +140 -0
  256. data/lib/omnizip/formats/format_spec_loader.rb +230 -0
  257. data/lib/omnizip/formats/gzip.rb +238 -0
  258. data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
  259. data/lib/omnizip/formats/iso/directory_record.rb +152 -0
  260. data/lib/omnizip/formats/iso/joliet.rb +204 -0
  261. data/lib/omnizip/formats/iso/path_table.rb +125 -0
  262. data/lib/omnizip/formats/iso/reader.rb +197 -0
  263. data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
  264. data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
  265. data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
  266. data/lib/omnizip/formats/iso/writer.rb +530 -0
  267. data/lib/omnizip/formats/iso.rb +140 -0
  268. data/lib/omnizip/formats/lzip.rb +175 -0
  269. data/lib/omnizip/formats/lzma_alone.rb +171 -0
  270. data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
  271. data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
  272. data/lib/omnizip/formats/rar/block_parser.rb +243 -0
  273. data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
  274. data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
  275. data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
  276. data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
  277. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
  278. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
  279. data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
  280. data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
  281. data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
  282. data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
  283. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
  284. data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
  285. data/lib/omnizip/formats/rar/constants.rb +82 -0
  286. data/lib/omnizip/formats/rar/decompressor.rb +238 -0
  287. data/lib/omnizip/formats/rar/external_writer.rb +312 -0
  288. data/lib/omnizip/formats/rar/header.rb +192 -0
  289. data/lib/omnizip/formats/rar/license_validator.rb +109 -0
  290. data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
  291. data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
  292. data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
  293. data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
  294. data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
  295. data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
  296. data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
  297. data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
  298. data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
  299. data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
  300. data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
  301. data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
  302. data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
  303. data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
  304. data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
  305. data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
  306. data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
  307. data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
  308. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
  309. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
  310. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
  311. data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
  312. data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
  313. data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
  314. data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
  315. data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
  316. data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
  317. data/lib/omnizip/formats/rar/reader.rb +366 -0
  318. data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
  319. data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
  320. data/lib/omnizip/formats/rar/writer.rb +431 -0
  321. data/lib/omnizip/formats/rar.rb +205 -0
  322. data/lib/omnizip/formats/rar3/compressor.rb +73 -0
  323. data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
  324. data/lib/omnizip/formats/rar3/reader.rb +386 -0
  325. data/lib/omnizip/formats/rar3/writer.rb +219 -0
  326. data/lib/omnizip/formats/rar5/compressor.rb +73 -0
  327. data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
  328. data/lib/omnizip/formats/rar5/reader.rb +342 -0
  329. data/lib/omnizip/formats/rar5/writer.rb +214 -0
  330. data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
  331. data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
  332. data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
  333. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
  334. data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
  335. data/lib/omnizip/formats/seven_zip/header.rb +106 -0
  336. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
  337. data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
  338. data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
  339. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
  340. data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
  341. data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
  342. data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
  343. data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
  344. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
  345. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
  346. data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
  347. data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
  348. data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
  349. data/lib/omnizip/formats/seven_zip.rb +93 -0
  350. data/lib/omnizip/formats/tar/constants.rb +73 -0
  351. data/lib/omnizip/formats/tar/entry.rb +94 -0
  352. data/lib/omnizip/formats/tar/header.rb +168 -0
  353. data/lib/omnizip/formats/tar/reader.rb +121 -0
  354. data/lib/omnizip/formats/tar/writer.rb +216 -0
  355. data/lib/omnizip/formats/tar.rb +84 -0
  356. data/lib/omnizip/formats/xz/reader.rb +116 -0
  357. data/lib/omnizip/formats/xz.rb +237 -0
  358. data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
  359. data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
  360. data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
  361. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
  362. data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
  363. data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
  364. data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
  365. data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
  366. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
  367. data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
  368. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
  369. data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
  370. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
  371. data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
  372. data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
  373. data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
  374. data/lib/omnizip/formats/zip/constants.rb +69 -0
  375. data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
  376. data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
  377. data/lib/omnizip/formats/zip/reader.rb +250 -0
  378. data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
  379. data/lib/omnizip/formats/zip/writer.rb +375 -0
  380. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
  381. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
  382. data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
  383. data/lib/omnizip/formats/zip.rb +50 -0
  384. data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
  385. data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
  386. data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
  387. data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
  388. data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
  389. data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
  390. data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
  391. data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
  392. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
  393. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
  394. data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
  395. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
  396. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
  397. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
  398. data/lib/omnizip/io/buffered_input.rb +146 -0
  399. data/lib/omnizip/io/buffered_output.rb +105 -0
  400. data/lib/omnizip/io/stream_manager.rb +115 -0
  401. data/lib/omnizip/link_handler/hard_link.rb +79 -0
  402. data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
  403. data/lib/omnizip/link_handler.rb +124 -0
  404. data/lib/omnizip/metadata/archive_metadata.rb +114 -0
  405. data/lib/omnizip/metadata/entry_metadata.rb +146 -0
  406. data/lib/omnizip/metadata/metadata_editor.rb +171 -0
  407. data/lib/omnizip/metadata/metadata_registry.rb +64 -0
  408. data/lib/omnizip/metadata/metadata_validator.rb +99 -0
  409. data/lib/omnizip/metadata.rb +57 -0
  410. data/lib/omnizip/models/.keep +0 -0
  411. data/lib/omnizip/models/algorithm_metadata.rb +73 -0
  412. data/lib/omnizip/models/compression_options.rb +71 -0
  413. data/lib/omnizip/models/conversion_options.rb +87 -0
  414. data/lib/omnizip/models/conversion_result.rb +135 -0
  415. data/lib/omnizip/models/eta_result.rb +46 -0
  416. data/lib/omnizip/models/extraction_rule.rb +115 -0
  417. data/lib/omnizip/models/filter_chain.rb +144 -0
  418. data/lib/omnizip/models/filter_config.rb +183 -0
  419. data/lib/omnizip/models/match_result.rb +124 -0
  420. data/lib/omnizip/models/optimization_suggestion.rb +91 -0
  421. data/lib/omnizip/models/parallel_options.rb +104 -0
  422. data/lib/omnizip/models/performance_result.rb +79 -0
  423. data/lib/omnizip/models/profile_report.rb +82 -0
  424. data/lib/omnizip/models/progress_options.rb +38 -0
  425. data/lib/omnizip/models/split_options.rb +116 -0
  426. data/lib/omnizip/optimization_registry.rb +81 -0
  427. data/lib/omnizip/parallel/job_queue.rb +209 -0
  428. data/lib/omnizip/parallel/job_scheduler.rb +203 -0
  429. data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
  430. data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
  431. data/lib/omnizip/parallel/worker_pool.rb +223 -0
  432. data/lib/omnizip/parallel.rb +149 -0
  433. data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
  434. data/lib/omnizip/parity/galois16.rb +145 -0
  435. data/lib/omnizip/parity/models/creator_packet.rb +73 -0
  436. data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
  437. data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
  438. data/lib/omnizip/parity/models/main_packet.rb +128 -0
  439. data/lib/omnizip/parity/models/packet.rb +156 -0
  440. data/lib/omnizip/parity/models/packet_registry.rb +109 -0
  441. data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
  442. data/lib/omnizip/parity/par2_creator.rb +531 -0
  443. data/lib/omnizip/parity/par2_repairer.rb +407 -0
  444. data/lib/omnizip/parity/par2_verifier.rb +364 -0
  445. data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
  446. data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
  447. data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
  448. data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
  449. data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
  450. data/lib/omnizip/parity.rb +186 -0
  451. data/lib/omnizip/password/encryption_registry.rb +65 -0
  452. data/lib/omnizip/password/encryption_strategy.rb +96 -0
  453. data/lib/omnizip/password/password_validator.rb +129 -0
  454. data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
  455. data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
  456. data/lib/omnizip/password.rb +87 -0
  457. data/lib/omnizip/pipe/stream_compressor.rb +124 -0
  458. data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
  459. data/lib/omnizip/pipe.rb +121 -0
  460. data/lib/omnizip/platform/ntfs_streams.rb +201 -0
  461. data/lib/omnizip/platform.rb +189 -0
  462. data/lib/omnizip/profile/archive_profile.rb +39 -0
  463. data/lib/omnizip/profile/balanced_profile.rb +33 -0
  464. data/lib/omnizip/profile/binary_profile.rb +36 -0
  465. data/lib/omnizip/profile/compression_profile.rb +158 -0
  466. data/lib/omnizip/profile/custom_profile.rb +157 -0
  467. data/lib/omnizip/profile/fast_profile.rb +33 -0
  468. data/lib/omnizip/profile/maximum_profile.rb +33 -0
  469. data/lib/omnizip/profile/profile_detector.rb +110 -0
  470. data/lib/omnizip/profile/profile_registry.rb +161 -0
  471. data/lib/omnizip/profile/text_profile.rb +36 -0
  472. data/lib/omnizip/profile.rb +190 -0
  473. data/lib/omnizip/profiler/memory_profiler.rb +66 -0
  474. data/lib/omnizip/profiler/method_profiler.rb +49 -0
  475. data/lib/omnizip/profiler/report_generator.rb +169 -0
  476. data/lib/omnizip/profiler.rb +204 -0
  477. data/lib/omnizip/progress/callback_reporter.rb +36 -0
  478. data/lib/omnizip/progress/console_reporter.rb +62 -0
  479. data/lib/omnizip/progress/log_reporter.rb +91 -0
  480. data/lib/omnizip/progress/operation_progress.rb +118 -0
  481. data/lib/omnizip/progress/progress_bar.rb +156 -0
  482. data/lib/omnizip/progress/progress_reporter.rb +40 -0
  483. data/lib/omnizip/progress/progress_tracker.rb +190 -0
  484. data/lib/omnizip/progress/silent_reporter.rb +24 -0
  485. data/lib/omnizip/progress.rb +127 -0
  486. data/lib/omnizip/rubyzip_compat.rb +63 -0
  487. data/lib/omnizip/temp/safe_extract.rb +168 -0
  488. data/lib/omnizip/temp/temp_file.rb +124 -0
  489. data/lib/omnizip/temp/temp_file_pool.rb +109 -0
  490. data/lib/omnizip/temp.rb +181 -0
  491. data/lib/omnizip/version.rb +5 -0
  492. data/lib/omnizip/zip/entry.rb +156 -0
  493. data/lib/omnizip/zip/file.rb +485 -0
  494. data/lib/omnizip/zip/input_stream.rb +273 -0
  495. data/lib/omnizip/zip/output_stream.rb +324 -0
  496. data/lib/omnizip.rb +156 -0
  497. data/readme-docs/advanced-features.adoc +515 -0
  498. data/readme-docs/api-usage.adoc +444 -0
  499. data/readme-docs/architecture.adoc +449 -0
  500. data/readme-docs/archive-formats.adoc +479 -0
  501. data/readme-docs/cli-usage.adoc +222 -0
  502. data/readme-docs/compression-algorithms.adoc +442 -0
  503. data/readme-docs/compression-profiles.adoc +247 -0
  504. data/readme-docs/encryption-checksums.adoc +328 -0
  505. data/readme-docs/format-converter.adoc +325 -0
  506. data/readme-docs/installation.adoc +228 -0
  507. data/readme-docs/par2-archives.adoc +608 -0
  508. data/readme-docs/performance-profiler.adoc +389 -0
  509. data/readme-docs/preprocessing-filters.adoc +280 -0
  510. data/xz-file-format-1.2.1.txt +1174 -0
  511. metadata +617 -0
@@ -0,0 +1,359 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ # Convenience methods for common archive operations
5
+ module Convenience
6
+ # Compress a single file to a ZIP archive
7
+ # @param input_path [String] Path to input file
8
+ # @param output_path [String] Path to output ZIP file
9
+ # @param options [Hash] Compression options
10
+ # @option options [Symbol, Omnizip::Profile::CompressionProfile] :profile
11
+ # Compression profile (:fast, :balanced, :maximum, :text, :binary, :archive, :auto)
12
+ # @option options [Symbol] :compression Compression method (:deflate, :bzip2, :lzma, :zstandard)
13
+ # @option options [Integer] :level Compression level (1-9)
14
+ # @option options [Boolean] :chunked Use chunked processing for large files
15
+ # @option options [Integer] :chunk_size Chunk size in bytes (default: 64MB)
16
+ # @option options [Integer] :max_memory Maximum memory usage (default: 256MB)
17
+ # @option options [Proc] :progress Progress callback
18
+ # @return [String] Path to created archive
19
+ #
20
+ # @example
21
+ # Omnizip.compress_file('document.txt', 'document.zip')
22
+ # Omnizip.compress_file('image.png', 'image.zip', compression: :lzma, level: 9)
23
+ # Omnizip.compress_file('huge.dat', 'huge.zip', chunked: true, max_memory: 128.megabytes)
24
+ # Omnizip.compress_file('data.txt', 'data.zip', profile: :fast)
25
+ # Omnizip.compress_file('app.exe', 'app.zip', profile: :auto)
26
+ def compress_file(input_path, output_path, **options)
27
+ unless ::File.exist?(input_path)
28
+ raise Errno::ENOENT,
29
+ "Input file not found: #{input_path}"
30
+ end
31
+ if ::File.directory?(input_path)
32
+ raise ArgumentError,
33
+ "Input is a directory: #{input_path}"
34
+ end
35
+
36
+ # Apply profile settings if specified
37
+ options = apply_profile(input_path, options) if options[:profile]
38
+
39
+ # Use chunked processing for large files if requested
40
+ if options[:chunked]
41
+ return Omnizip::Chunked.compress_file(input_path, output_path,
42
+ **options)
43
+ end
44
+
45
+ Omnizip::Zip::File.create(output_path) do |zip|
46
+ basename = ::File.basename(input_path)
47
+ zip.add(basename, input_path)
48
+ end
49
+
50
+ output_path
51
+ end
52
+
53
+ # Compress a directory to a ZIP archive
54
+ # @param input_dir [String] Path to input directory
55
+ # @param output_path [String] Path to output ZIP file
56
+ # @param options [Hash] Compression options
57
+ # @option options [Symbol, Omnizip::Profile::CompressionProfile] :profile
58
+ # Compression profile (:fast, :balanced, :maximum, etc.)
59
+ # @option options [Symbol] :compression Compression method
60
+ # @option options [Integer] :level Compression level (1-9)
61
+ # @option options [Boolean] :recursive Include subdirectories (default: true)
62
+ # @option options [Integer] :max_memory Maximum memory usage for large files
63
+ # @option options [Proc] :progress Progress callback
64
+ # @return [String] Path to created archive
65
+ #
66
+ # @example
67
+ # Omnizip.compress_directory('project/', 'backup.zip')
68
+ # Omnizip.compress_directory('src/', 'src.zip', compression: :lzma2, level: 9)
69
+ # Omnizip.compress_directory('large/', 'backup.zip', max_memory: 256.megabytes)
70
+ # Omnizip.compress_directory('src/', 'backup.7z', profile: :maximum)
71
+ def compress_directory(input_dir, output_path, recursive: true, **options)
72
+ unless ::File.exist?(input_dir)
73
+ raise Errno::ENOENT,
74
+ "Input directory not found: #{input_dir}"
75
+ end
76
+ unless ::File.directory?(input_dir)
77
+ raise ArgumentError,
78
+ "Input is not a directory: #{input_dir}"
79
+ end
80
+
81
+ # Apply profile settings if specified (use first file for auto-detection)
82
+ if options[:profile]
83
+ first_file = find_first_file(input_dir)
84
+ apply_profile(first_file, options)
85
+ end
86
+
87
+ Omnizip::Zip::File.create(output_path) do |zip|
88
+ add_directory_contents(zip, input_dir, "", recursive: recursive)
89
+ end
90
+
91
+ output_path
92
+ end
93
+
94
+ # Extract a ZIP archive to a directory
95
+ # @param archive_path [String] Path to ZIP archive
96
+ # @param output_dir [String] Path to output directory
97
+ # @param options [Hash] Extraction options
98
+ # @option options [Boolean] :overwrite Overwrite existing files (default: false)
99
+ # @return [Array<String>] List of extracted file paths
100
+ #
101
+ # @example
102
+ # Omnizip.extract_archive('backup.zip', 'restore/')
103
+ # Omnizip.extract_archive('archive.zip', 'output/', overwrite: true)
104
+ def extract_archive(archive_path, output_dir, overwrite: false, **_options)
105
+ unless ::File.exist?(archive_path)
106
+ raise Errno::ENOENT,
107
+ "Archive not found: #{archive_path}"
108
+ end
109
+
110
+ extracted_files = []
111
+
112
+ Omnizip::Zip::File.open(archive_path) do |zip|
113
+ zip.each do |entry|
114
+ dest_path = ::File.join(output_dir, entry.name)
115
+
116
+ # Handle overwrite option
117
+ on_exists = if overwrite
118
+ proc { true }
119
+ else
120
+ proc { |_e, path| raise "File exists: #{path}" }
121
+ end
122
+
123
+ zip.extract(entry, dest_path, &on_exists)
124
+ extracted_files << dest_path
125
+ end
126
+ end
127
+
128
+ extracted_files
129
+ end
130
+
131
+ # List contents of a ZIP archive
132
+ # @param archive_path [String] Path to ZIP archive
133
+ # @param options [Hash] Listing options
134
+ # @option options [Boolean] :details Include detailed information (default: false)
135
+ # @return [Array<String>, Array<Hash>] List of entry names or detailed info
136
+ #
137
+ # @example
138
+ # Omnizip.list_archive('backup.zip')
139
+ # # => ["file1.txt", "file2.txt", "dir/"]
140
+ #
141
+ # Omnizip.list_archive('backup.zip', details: true)
142
+ # # => [{name: "file1.txt", size: 1024, compressed_size: 512, ...}, ...]
143
+ def list_archive(archive_path, details: false, **_options)
144
+ unless ::File.exist?(archive_path)
145
+ raise Errno::ENOENT,
146
+ "Archive not found: #{archive_path}"
147
+ end
148
+
149
+ Omnizip::Zip::File.open(archive_path) do |zip|
150
+ if details
151
+ zip.entries.map do |entry|
152
+ {
153
+ name: entry.name,
154
+ size: entry.size,
155
+ compressed_size: entry.compressed_size,
156
+ compression_method: entry.compression_method,
157
+ crc: entry.crc,
158
+ time: entry.time,
159
+ directory: entry.directory?,
160
+ }
161
+ end
162
+ else
163
+ zip.names
164
+ end
165
+ end
166
+ end
167
+
168
+ # Read a single file from a ZIP archive
169
+ # @param archive_path [String] Path to ZIP archive
170
+ # @param entry_name [String] Name of entry to read
171
+ # @return [String] Contents of the file
172
+ #
173
+ # @example
174
+ # content = Omnizip.read_from_archive('backup.zip', 'config.yml')
175
+ def read_from_archive(archive_path, entry_name)
176
+ unless ::File.exist?(archive_path)
177
+ raise Errno::ENOENT,
178
+ "Archive not found: #{archive_path}"
179
+ end
180
+
181
+ Omnizip::Zip::File.open(archive_path) do |zip|
182
+ entry = zip.get_entry(entry_name)
183
+ raise Errno::ENOENT, "Entry not found: #{entry_name}" unless entry
184
+
185
+ zip.read(entry)
186
+ end
187
+ end
188
+
189
+ # Add a file to an existing ZIP archive
190
+ # @param archive_path [String] Path to ZIP archive
191
+ # @param entry_name [String] Name for entry in archive
192
+ # @param source_path [String] Path to source file
193
+ # @return [String] Path to archive
194
+ #
195
+ # @example
196
+ # Omnizip.add_to_archive('backup.zip', 'new_file.txt', 'path/to/new_file.txt')
197
+ def add_to_archive(archive_path, entry_name, source_path)
198
+ unless ::File.exist?(archive_path)
199
+ raise Errno::ENOENT,
200
+ "Archive not found: #{archive_path}"
201
+ end
202
+ unless ::File.exist?(source_path)
203
+ raise Errno::ENOENT,
204
+ "Source file not found: #{source_path}"
205
+ end
206
+
207
+ Omnizip::Zip::File.open(archive_path) do |zip|
208
+ zip.add(entry_name, source_path)
209
+ end
210
+
211
+ archive_path
212
+ end
213
+
214
+ # Remove a file from a ZIP archive
215
+ # @param archive_path [String] Path to ZIP archive
216
+ # @param entry_name [String] Name of entry to remove
217
+ # @return [String] Path to archive
218
+ #
219
+ # @example
220
+ # Omnizip.remove_from_archive('backup.zip', 'old_file.txt')
221
+ def remove_from_archive(archive_path, entry_name)
222
+ unless ::File.exist?(archive_path)
223
+ raise Errno::ENOENT,
224
+ "Archive not found: #{archive_path}"
225
+ end
226
+
227
+ Omnizip::Zip::File.open(archive_path) do |zip|
228
+ zip.remove(entry_name)
229
+ end
230
+
231
+ archive_path
232
+ end
233
+
234
+ # Create a RAR archive
235
+ # @param archive_path [String] Path to output RAR file
236
+ # @param options [Hash] Creation options
237
+ # @option options [Integer] :version RAR version (4 or 5, default: 5 for pure Ruby)
238
+ # @option options [Symbol] :compression For RAR5: :store, :lzma, :auto (default: :store)
239
+ # @option options [Integer] :level For RAR5: LZMA compression level 1-5 (default: 3)
240
+ # @option options [Boolean] :include_mtime Include modification time (RAR5 only, default: false)
241
+ # @option options [Boolean] :include_crc32 Include CRC32 checksum (RAR5 only, default: false)
242
+ # @yield [writer] RAR writer instance
243
+ # @return [String] Path to created archive
244
+ #
245
+ # @example Create RAR5 archive with STORE compression
246
+ # Omnizip.create_rar('backup.rar') do |rar|
247
+ # rar.add_file('document.pdf')
248
+ # end
249
+ #
250
+ # @example Create RAR5 with LZMA compression
251
+ # Omnizip.create_rar('backup.rar', compression: :lzma, level: 5) do |rar|
252
+ # rar.add_file('data.txt')
253
+ # end
254
+ #
255
+ # @example Create RAR5 with optional fields
256
+ # Omnizip.create_rar('backup.rar',
257
+ # compression: :lzma,
258
+ # level: 3,
259
+ # include_mtime: true,
260
+ # include_crc32: true
261
+ # ) do |rar|
262
+ # rar.add_file('important.doc')
263
+ # end
264
+ # rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
265
+ def create_rar(archive_path, **options, &block)
266
+ # Default to RAR5 (pure Ruby) unless explicitly specified
267
+ options[:version] ||= 5
268
+
269
+ Omnizip::Formats::Rar.create(archive_path, options, &block)
270
+ end
271
+ # rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding
272
+
273
+ private
274
+
275
+ # Apply compression profile to options
276
+ #
277
+ # @param file_path [String, nil] File path for auto-detection
278
+ # @param options [Hash] Compression options
279
+ # @return [Hash] Updated options with profile settings
280
+ def apply_profile(file_path, options)
281
+ profile_spec = options.delete(:profile)
282
+ return options unless profile_spec
283
+
284
+ # Get the profile
285
+ profile = case profile_spec
286
+ when :auto
287
+ # Auto-detect based on file
288
+ file_path ? Omnizip::Profile.detect(file_path) : Omnizip::Profile.get(:balanced)
289
+ when Symbol
290
+ # Get by name
291
+ Omnizip::Profile.get(profile_spec) || Omnizip::Profile.get(:balanced)
292
+ when Omnizip::Profile::CompressionProfile
293
+ # Use the profile directly
294
+ profile_spec
295
+ else
296
+ Omnizip::Profile.get(:balanced)
297
+ end
298
+
299
+ # Apply profile to options
300
+ profile.apply_to(options)
301
+ end
302
+
303
+ # Find first file in directory for profile detection
304
+ #
305
+ # @param dir_path [String] Directory path
306
+ # @return [String, nil] Path to first file or nil
307
+ def find_first_file(dir_path)
308
+ Dir.foreach(dir_path) do |entry|
309
+ next if [".", ".."].include?(entry)
310
+
311
+ full_path = ::File.join(dir_path, entry)
312
+ return full_path if ::File.file?(full_path)
313
+
314
+ # Check subdirectories
315
+ if ::File.directory?(full_path)
316
+ result = find_first_file(full_path)
317
+ return result if result
318
+ end
319
+ end
320
+ nil
321
+ end
322
+
323
+ # Recursively add directory contents to archive
324
+ def add_directory_contents(zip, base_dir, relative_path, recursive: true)
325
+ dir_path = ::File.join(base_dir, relative_path)
326
+
327
+ Dir.foreach(dir_path) do |entry|
328
+ next if [".", ".."].include?(entry)
329
+
330
+ full_path = ::File.join(dir_path, entry)
331
+
332
+ # Create archive path (strip leading separator if at root)
333
+ archive_path = if relative_path.empty?
334
+ entry
335
+ else
336
+ ::File.join(relative_path, entry)
337
+ end
338
+
339
+ if ::File.directory?(full_path)
340
+ # Add directory entry with trailing slash
341
+ dir_entry_name = archive_path.end_with?("/") ? archive_path : "#{archive_path}/"
342
+ zip.add(dir_entry_name)
343
+
344
+ # Recursively add contents if requested
345
+ if recursive
346
+ add_directory_contents(zip, base_dir, archive_path,
347
+ recursive: recursive)
348
+ end
349
+ else
350
+ # Add file with its data
351
+ zip.add(archive_path, full_path)
352
+ end
353
+ end
354
+ end
355
+ end
356
+
357
+ # Extend Omnizip module with convenience methods
358
+ extend Convenience
359
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module Converter
5
+ # Registry for conversion strategies
6
+ class ConversionRegistry
7
+ @strategies = []
8
+
9
+ class << self
10
+ # Register a conversion strategy
11
+ # @param strategy_class [Class] Strategy class
12
+ def register(strategy_class)
13
+ @strategies << strategy_class unless @strategies.include?(strategy_class)
14
+ end
15
+
16
+ # Find strategy for source and target formats
17
+ # @param source [String] Source file path
18
+ # @param target [String] Target file path
19
+ # @return [Class, nil] Strategy class or nil
20
+ def find_strategy(source, target)
21
+ @strategies.find { |strategy| strategy.can_convert?(source, target) }
22
+ end
23
+
24
+ # Get all registered strategies
25
+ # @return [Array<Class>] List of strategy classes
26
+ def strategies
27
+ @strategies
28
+ end
29
+
30
+ # Check if conversion is supported
31
+ # @param source [String] Source file path
32
+ # @param target [String] Target file path
33
+ # @return [Boolean] True if supported
34
+ def supported?(source, target)
35
+ !find_strategy(source, target).nil?
36
+ end
37
+
38
+ # Reset registry (for testing)
39
+ def reset
40
+ @strategies = []
41
+ end
42
+ end
43
+ end
44
+
45
+ # Register built-in strategies
46
+ ConversionRegistry.register(ZipToSevenZipStrategy)
47
+ ConversionRegistry.register(SevenZipToZipStrategy)
48
+ end
49
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module Converter
5
+ # Base class for archive format conversion strategies
6
+ class ConversionStrategy
7
+ attr_reader :source_path, :target_path, :options
8
+
9
+ # Initialize conversion strategy
10
+ # @param source_path [String] Source archive path
11
+ # @param target_path [String] Target archive path
12
+ # @param options [ConversionOptions] Conversion options
13
+ def initialize(source_path, target_path, options)
14
+ @source_path = source_path
15
+ @target_path = target_path
16
+ @options = options
17
+ @warnings = []
18
+ end
19
+
20
+ # Perform the conversion
21
+ # @return [ConversionResult] Conversion result
22
+ # @raise [NotImplementedError] Subclasses must implement
23
+ def convert
24
+ raise NotImplementedError, "#{self.class} must implement #convert"
25
+ end
26
+
27
+ # Get source format
28
+ # @return [Symbol] Source format
29
+ # @raise [NotImplementedError] Subclasses must implement
30
+ def source_format
31
+ raise NotImplementedError, "#{self.class} must implement #source_format"
32
+ end
33
+
34
+ # Get target format
35
+ # @return [Symbol] Target format
36
+ # @raise [NotImplementedError] Subclasses must implement
37
+ def target_format
38
+ raise NotImplementedError, "#{self.class} must implement #target_format"
39
+ end
40
+
41
+ # Check if this strategy can handle the conversion
42
+ # @param source [String] Source file path
43
+ # @param target [String] Target file path
44
+ # @return [Boolean] True if can handle
45
+ def self.can_convert?(_source, _target)
46
+ false
47
+ end
48
+
49
+ protected
50
+
51
+ # Add a warning message
52
+ # @param message [String] Warning message
53
+ def add_warning(message)
54
+ @warnings << message
55
+ end
56
+
57
+ # Get all warnings
58
+ # @return [Array<String>] List of warnings
59
+ def warnings
60
+ @warnings
61
+ end
62
+
63
+ # Detect format from file extension
64
+ # @param path [String] File path
65
+ # @return [Symbol] Format
66
+ def detect_format(path)
67
+ ext = File.extname(path).downcase
68
+ case ext
69
+ when ".zip"
70
+ :zip
71
+ when ".7z"
72
+ :seven_zip
73
+ else
74
+ raise ArgumentError, "Unknown format for file: #{path}"
75
+ end
76
+ end
77
+
78
+ # Create conversion result
79
+ # @param start_time [Time] Start time
80
+ # @param entry_count [Integer] Number of entries
81
+ # @return [ConversionResult] Result object
82
+ def create_result(start_time, entry_count)
83
+ require_relative "../models/conversion_result"
84
+
85
+ duration = Time.now - start_time
86
+ source_size = File.size(source_path)
87
+ target_size = File.size(target_path)
88
+
89
+ Omnizip::Models::ConversionResult.new(
90
+ source_path: source_path,
91
+ target_path: target_path,
92
+ source_format: source_format,
93
+ target_format: target_format,
94
+ source_size: source_size,
95
+ target_size: target_size,
96
+ duration: duration,
97
+ entry_count: entry_count,
98
+ warnings: warnings,
99
+ )
100
+ end
101
+
102
+ # Check if metadata is compatible between formats
103
+ # @param entry [Entry] Entry to check
104
+ # @return [Boolean] True if fully compatible
105
+ def metadata_compatible?(_entry)
106
+ # ZIP supports most metadata
107
+ # 7z has limited metadata support
108
+ case [source_format, target_format]
109
+ when %i[zip seven_zip]
110
+ # Some metadata loss (comments, extra fields)
111
+ false
112
+ when %i[seven_zip zip]
113
+ # Can preserve most 7z metadata in ZIP
114
+ true
115
+ else
116
+ true
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "conversion_strategy"
4
+
5
+ module Omnizip
6
+ module Converter
7
+ # Convert 7-Zip archives to ZIP format
8
+ class SevenZipToZipStrategy < ConversionStrategy
9
+ # Perform 7z to ZIP conversion
10
+ # @return [ConversionResult] Conversion result
11
+ def convert
12
+ start_time = Time.now
13
+ 0
14
+
15
+ require_relative "../formats/seven_zip"
16
+ require_relative "../zip/file"
17
+
18
+ # Open source 7z archive
19
+ reader = Omnizip::Formats::SevenZip::Reader.new(source_path)
20
+ reader.read
21
+
22
+ # Collect entries
23
+ entries_data = collect_seven_zip_entries(reader)
24
+ entry_count = entries_data.size
25
+
26
+ # Create target ZIP archive
27
+ create_zip(entries_data)
28
+
29
+ create_result(start_time, entry_count)
30
+ end
31
+
32
+ # Get source format
33
+ # @return [Symbol] Source format (:seven_zip)
34
+ def source_format
35
+ :seven_zip
36
+ end
37
+
38
+ # Get target format
39
+ # @return [Symbol] Target format (:zip)
40
+ def target_format
41
+ :zip
42
+ end
43
+
44
+ # Check if can convert
45
+ # @param source [String] Source file
46
+ # @param target [String] Target file
47
+ # @return [Boolean] True if can convert
48
+ def self.can_convert?(source, target)
49
+ source.end_with?(".7z") && target.end_with?(".zip")
50
+ end
51
+
52
+ private
53
+
54
+ def collect_seven_zip_entries(reader)
55
+ entries = []
56
+
57
+ reader.entries.each do |entry|
58
+ data = {
59
+ name: entry.name,
60
+ content: nil,
61
+ mtime: entry.mtime || Time.now,
62
+ }
63
+
64
+ # Extract entry data
65
+ unless entry.name.end_with?("/")
66
+ File.open(source_path, "rb") do |io|
67
+ data[:content] = reader.send(:extract_entry_data, io, entry)
68
+ end
69
+ end
70
+
71
+ entries << data
72
+ end
73
+
74
+ entries
75
+ end
76
+
77
+ def create_zip(entries)
78
+ Omnizip::Zip::File.create(target_path) do |zip|
79
+ entries.each do |entry_data|
80
+ if entry_data[:name].end_with?("/")
81
+ # Directory entry
82
+ zip.add(entry_data[:name])
83
+ else
84
+ # File entry
85
+ zip.add(entry_data[:name]) { entry_data[:content] }
86
+ end
87
+ end
88
+
89
+ # Set archive comment if preserving metadata
90
+ if options.preserve_metadata
91
+ zip.comment = "Converted from 7z by Omnizip"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end