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,292 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module Formats
5
+ module Rar
6
+ # RAR parity operations handler
7
+ # Reads parity data and performs recovery calculations
8
+ class ParityHandler
9
+ attr_reader :recovery_record, :parity_blocks
10
+
11
+ # Initialize parity handler
12
+ #
13
+ # @param recovery_record [RecoveryRecord] Recovery record instance
14
+ def initialize(recovery_record)
15
+ @recovery_record = recovery_record
16
+ @parity_blocks = []
17
+ end
18
+
19
+ # Load parity data from .rev files
20
+ #
21
+ # @param rev_files [Array<String>] Paths to .rev files
22
+ # @return [Boolean] true if loaded successfully
23
+ def load_parity_data(rev_files)
24
+ @parity_blocks = []
25
+
26
+ rev_files.each do |rev_file|
27
+ blocks = parse_rev_file(rev_file)
28
+ @parity_blocks.concat(blocks) if blocks
29
+ end
30
+
31
+ @parity_blocks.any?
32
+ end
33
+
34
+ # Verify archive blocks using parity
35
+ #
36
+ # @param archive_path [String] Path to archive
37
+ # @param block_indices [Array<Integer>] Blocks to verify
38
+ # @return [Hash] Verification results
39
+ def verify_blocks(archive_path, block_indices = nil)
40
+ if @parity_blocks.empty?
41
+ return { verified: false,
42
+ error: "No parity data" }
43
+ end
44
+
45
+ blocks_to_check = block_indices || (0...total_blocks).to_a
46
+ corrupted_blocks = []
47
+
48
+ blocks_to_check.each do |index|
49
+ corrupted_blocks << index unless verify_block(archive_path, index)
50
+ end
51
+
52
+ {
53
+ verified: corrupted_blocks.empty?,
54
+ total: blocks_to_check.size,
55
+ ok: blocks_to_check.size - corrupted_blocks.size,
56
+ corrupted: corrupted_blocks,
57
+ }
58
+ end
59
+
60
+ # Calculate parity for a block
61
+ #
62
+ # @param data [String] Binary data
63
+ # @param block_index [Integer] Block index
64
+ # @return [String] Parity data
65
+ def calculate_parity(data, block_index)
66
+ # Basic XOR parity for simple recovery
67
+ # In production, would use Reed-Solomon
68
+ parity = data.bytes.reduce(0) { |acc, byte| acc ^ byte }
69
+ [parity, block_index].pack("CN")
70
+ end
71
+
72
+ # Recover corrupted block
73
+ #
74
+ # @param archive_path [String] Path to archive
75
+ # @param block_index [Integer] Block to recover
76
+ # @return [String, nil] Recovered data or nil
77
+ def recover_block(archive_path, block_index)
78
+ return nil unless can_recover?(block_index)
79
+
80
+ if @recovery_record.version == 5
81
+ recover_rar5_block(archive_path, block_index)
82
+ else
83
+ recover_rar4_block(archive_path, block_index)
84
+ end
85
+ end
86
+
87
+ # Check if block can be recovered
88
+ #
89
+ # @param block_index [Integer] Block index
90
+ # @return [Boolean] true if recoverable
91
+ def can_recover?(block_index)
92
+ return false if @parity_blocks.empty?
93
+
94
+ # Can recover if we have parity data for this block
95
+ @parity_blocks.any? { |p| p[:index] == block_index }
96
+ end
97
+
98
+ # Get total number of blocks
99
+ #
100
+ # @return [Integer] Total blocks
101
+ def total_blocks
102
+ return 0 if @recovery_record.block_size.zero?
103
+
104
+ (@recovery_record.protected_size / @recovery_record.block_size.to_f).ceil
105
+ end
106
+
107
+ # Get parity block by index
108
+ #
109
+ # @param index [Integer] Block index
110
+ # @return [Hash, nil] Parity block data
111
+ def parity_block(index)
112
+ @parity_blocks.find { |p| p[:index] == index }
113
+ end
114
+
115
+ private
116
+
117
+ # Parse .rev file
118
+ #
119
+ # @param rev_file [String] Path to .rev file
120
+ # @return [Array<Hash>, nil] Parity blocks
121
+ def parse_rev_file(rev_file)
122
+ return nil unless File.exist?(rev_file)
123
+
124
+ blocks = []
125
+
126
+ File.open(rev_file, "rb") do |io|
127
+ # Check RAR signature in .rev file
128
+ signature = io.read(7)
129
+ return nil unless valid_rev_signature?(signature)
130
+
131
+ # Parse recovery blocks
132
+ until io.eof?
133
+ block = parse_parity_block(io)
134
+ break unless block
135
+
136
+ blocks << block
137
+ end
138
+ end
139
+
140
+ blocks
141
+ rescue StandardError
142
+ nil
143
+ end
144
+
145
+ # Check if valid .rev file signature
146
+ #
147
+ # @param signature [String] File signature
148
+ # @return [Boolean] true if valid
149
+ def valid_rev_signature?(signature)
150
+ return false unless signature
151
+
152
+ bytes = signature.bytes
153
+ bytes[0..3] == [0x52, 0x61, 0x72, 0x21] # "Rar!"
154
+ end
155
+
156
+ # Parse parity block from .rev file
157
+ #
158
+ # @param io [IO] Input stream
159
+ # @return [Hash, nil] Parity block data
160
+ def parse_parity_block(io)
161
+ # Read block header
162
+ block_size = io.read(4)&.unpack1("V")
163
+ return nil unless block_size
164
+
165
+ block_index = io.read(4)&.unpack1("V")
166
+ checksum = io.read(4)&.unpack1("V")
167
+
168
+ # Read parity data
169
+ parity_data = io.read(block_size - 12)
170
+ return nil unless parity_data
171
+
172
+ {
173
+ index: block_index,
174
+ size: block_size,
175
+ checksum: checksum,
176
+ data: parity_data,
177
+ }
178
+ rescue StandardError
179
+ nil
180
+ end
181
+
182
+ # Verify single block
183
+ #
184
+ # @param archive_path [String] Path to archive
185
+ # @param block_index [Integer] Block index
186
+ # @return [Boolean] true if block is valid
187
+ def verify_block(archive_path, block_index)
188
+ parity = parity_block(block_index)
189
+ return true unless parity # No parity, assume OK
190
+
191
+ # Read block from archive
192
+ block_data = read_archive_block(archive_path, block_index)
193
+ return false unless block_data
194
+
195
+ # Calculate checksum
196
+ calculated = calculate_block_checksum(block_data)
197
+ calculated == parity[:checksum]
198
+ end
199
+
200
+ # Read block from archive
201
+ #
202
+ # @param archive_path [String] Path to archive
203
+ # @param block_index [Integer] Block index
204
+ # @return [String, nil] Block data
205
+ def read_archive_block(archive_path, block_index)
206
+ return nil unless File.exist?(archive_path)
207
+
208
+ block_size = @recovery_record.block_size
209
+ offset = block_index * block_size
210
+
211
+ File.open(archive_path, "rb") do |io|
212
+ io.seek(offset)
213
+ io.read(block_size)
214
+ end
215
+ rescue StandardError
216
+ nil
217
+ end
218
+
219
+ # Calculate block checksum
220
+ #
221
+ # @param data [String] Block data
222
+ # @return [Integer] CRC32 checksum
223
+ def calculate_block_checksum(data)
224
+ Zlib.crc32(data)
225
+ end
226
+
227
+ # Recover RAR5 block using Reed-Solomon
228
+ #
229
+ # @param archive_path [String] Path to archive
230
+ # @param block_index [Integer] Block index
231
+ # @return [String, nil] Recovered data
232
+ def recover_rar5_block(archive_path, block_index)
233
+ parity = parity_block(block_index)
234
+ return nil unless parity
235
+
236
+ # Read corrupted block
237
+ block_data = read_archive_block(archive_path, block_index)
238
+ return nil unless block_data
239
+
240
+ # Use Reed-Solomon decoding
241
+ # This is a placeholder - real implementation would use
242
+ # reed-solomon gem or similar
243
+ recover_with_reed_solomon(block_data, parity[:data])
244
+ end
245
+
246
+ # Recover RAR4 block using old recovery method
247
+ #
248
+ # @param archive_path [String] Path to archive
249
+ # @param block_index [Integer] Block index
250
+ # @return [String, nil] Recovered data
251
+ def recover_rar4_block(archive_path, block_index)
252
+ parity = parity_block(block_index)
253
+ return nil unless parity
254
+
255
+ # Read corrupted block
256
+ block_data = read_archive_block(archive_path, block_index)
257
+ return nil unless block_data
258
+
259
+ # Simple XOR recovery for RAR4
260
+ recover_with_xor(block_data, parity[:data])
261
+ end
262
+
263
+ # Recover data using Reed-Solomon (RAR5)
264
+ #
265
+ # @param corrupted_data [String] Corrupted block
266
+ # @param parity_data [String] Parity information
267
+ # @return [String, nil] Recovered data
268
+ def recover_with_reed_solomon(_corrupted_data, _parity_data)
269
+ # Placeholder for Reed-Solomon recovery
270
+ # Real implementation would use reed-solomon gem
271
+ # For now, return nil to indicate recovery not available
272
+ nil
273
+ end
274
+
275
+ # Recover data using XOR (RAR4)
276
+ #
277
+ # @param corrupted_data [String] Corrupted block
278
+ # @param parity_data [String] Parity information
279
+ # @return [String] Recovered data
280
+ def recover_with_xor(corrupted_data, parity_data)
281
+ # Simple XOR recovery
282
+ result = corrupted_data.bytes.map.with_index do |byte, i|
283
+ parity_byte = parity_data.bytes[i % parity_data.size]
284
+ byte ^ parity_byte
285
+ end
286
+
287
+ result.pack("C*")
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+ require_relative "../../../../algorithms/lzma"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module Rar
9
+ module Rar5
10
+ module Compression
11
+ # LZMA compression method for RAR5
12
+ #
13
+ # This adapter uses the existing LZMA implementation with
14
+ # RAR5-specific parameters and encoding.
15
+ #
16
+ # RAR5 compression methods (from spec):
17
+ # - 0: STORE (no compression)
18
+ # - 1: FASTEST
19
+ # - 2: FAST
20
+ # - 3: NORMAL
21
+ # - 4: GOOD
22
+ # - 5: BEST
23
+ #
24
+ # For methods 1-5, RAR5 uses LZMA compression with different
25
+ # dictionary sizes and compression levels.
26
+ #
27
+ # @example Compress data with LZMA
28
+ # compressed = Lzma.compress("Hello, World!", level: 5)
29
+ class Lzma
30
+ # Compression method identifier for RAR5
31
+ # Methods 1-5 all use LZMA with different parameters
32
+ METHOD_FASTEST = 1
33
+ METHOD_FAST = 2
34
+ METHOD_NORMAL = 3
35
+ METHOD_GOOD = 4
36
+ METHOD_BEST = 5
37
+
38
+ # Compress data using LZMA
39
+ #
40
+ # @param data [String] Data to compress
41
+ # @param options [Hash] Compression options
42
+ # @option options [Integer] :level Compression level (1-5, default: 3)
43
+ # @return [Hash] Hash with :data (compressed) and :properties (9 bytes for extra area)
44
+ def self.compress(data, options = {})
45
+ level = options[:level] || METHOD_NORMAL
46
+
47
+ # Create StringIO streams for LZMA
48
+ input = StringIO.new(data)
49
+ output = StringIO.new
50
+ output.set_encoding(Encoding::BINARY)
51
+
52
+ # Create LZMA encoder with RAR5-appropriate settings
53
+ lzma = Algorithms::LZMA.new
54
+ lzma_options = build_lzma_options(level)
55
+
56
+ # Build encoder options hash that LZMA encoder will accept
57
+ encoder_options = {
58
+ dict_size: lzma_options.dict_size,
59
+ lc: lzma_options.lc,
60
+ lp: lzma_options.lp,
61
+ pb: lzma_options.pb,
62
+ level: level,
63
+ }
64
+
65
+ # Compress using LZMA with RAR5 parameters
66
+ lzma.compress(input, output, encoder_options)
67
+
68
+ # The LZMA encoder outputs:
69
+ # - 1 byte: properties (lc, lp, pb)
70
+ # - 4 bytes: dictionary size (little-endian)
71
+ # - 8 bytes: uncompressed size (little-endian)
72
+ # - remaining: compressed data
73
+ #
74
+ # RAR5 stores properties in file header extra area (type 0x03),
75
+ # so we extract them separately
76
+ compressed_with_header = output.string
77
+
78
+ if compressed_with_header.bytesize > 13
79
+ # Extract LZMA properties for RAR5 extra area
80
+ # RAR5 stores: property byte (1) + dict size (4) = 5 bytes
81
+ # NOT the uncompressed size (which is in the next 8 bytes of LZMA header)
82
+ properties = compressed_with_header[0, 5]
83
+
84
+ # Extract raw LZMA stream (skip 13-byte header)
85
+ compressed_data = compressed_with_header[13..]
86
+
87
+ {
88
+ data: compressed_data,
89
+ properties: properties,
90
+ }
91
+ else
92
+ # Edge case: if compression produced less than 13 bytes (extremely rare),
93
+ # return original data uncompressed with nil properties
94
+ warn "LZMA output too small (#{compressed_with_header.bytesize} bytes), using STORE"
95
+ {
96
+ data: data,
97
+ properties: nil,
98
+ }
99
+ end
100
+ end
101
+
102
+ # Decompress LZMA-compressed data
103
+ #
104
+ # @param data [String] Compressed data (raw LZMA stream without header)
105
+ # @param options [Hash] Decompression options
106
+ # @option options [String] :properties The 5-byte properties from compress (property byte + dict size)
107
+ # @option options [Integer] :uncompressed_size Expected uncompressed size (optional, for EOS marker mode)
108
+ # @return [String] Decompressed data
109
+ def self.decompress(data, options = {})
110
+ properties = options[:properties]
111
+ uncompressed_size = options[:uncompressed_size]
112
+
113
+ # Reconstruct the 13-byte LZMA header if properties are provided
114
+ if properties && properties.bytesize >= 5
115
+ # properties contains: 1 byte props + 4 bytes dict size
116
+ # We need to add 8 bytes for uncompressed size
117
+ header = properties.dup
118
+ header += if uncompressed_size
119
+ # Add uncompressed size as 8-byte little-endian
120
+ [uncompressed_size].pack("Q<")
121
+ else
122
+ # Use -1 (0xFFFFFFFFFFFFFFFF) to indicate EOS marker mode
123
+ [0xFFFFFFFFFFFFFFFF].pack("Q<")
124
+ end
125
+ full_data = header + data
126
+ else
127
+ # Assume data already has header (backward compatibility)
128
+ full_data = data
129
+ end
130
+
131
+ input = StringIO.new(full_data)
132
+ output = StringIO.new
133
+ output.set_encoding(Encoding::BINARY)
134
+
135
+ # Use SDK decoder since RAR5 LZMA was compressed with SDK encoder
136
+ require_relative "../../../../implementations/seven_zip/lzma/decoder"
137
+ decoder = Implementations::SevenZip::LZMA::Decoder.new(input)
138
+ decoder.decode_stream(output)
139
+
140
+ output.string
141
+ end
142
+
143
+ # Get compression method identifier for level
144
+ #
145
+ # @param level [Integer] Compression level (1-5)
146
+ # @return [Integer] Method ID
147
+ def self.method_id(level = METHOD_NORMAL)
148
+ level.clamp(METHOD_FASTEST, METHOD_BEST)
149
+ end
150
+
151
+ # Get compression info VINT value
152
+ #
153
+ # For RAR5, compression_info encodes:
154
+ # - Bits 0-5: compression method (1-5 for LZMA)
155
+ # - Bits 6+: version (0 for now)
156
+ #
157
+ # @param level [Integer] Compression level (1-5)
158
+ # @return [Integer] Compression info value
159
+ def self.compression_info(level = METHOD_NORMAL)
160
+ method = method_id(level)
161
+ method & 0x3F # Bits 0-5 only
162
+ end
163
+
164
+ # Build LZMA options based on RAR5 compression level
165
+ #
166
+ # @param level [Integer] RAR5 compression level (1-5)
167
+ # @return [LzmaOptions] LZMA encoder options object
168
+ def self.build_lzma_options(level)
169
+ # RAR5 compression levels map to LZMA parameters
170
+ # These are approximations based on typical RAR behavior
171
+ dict_size = 1 << case level
172
+ when 1 then 18 # 256 KB (fastest)
173
+ when 2 then 20 # 1 MB (fast)
174
+ when 3 then 22 # 4 MB (normal)
175
+ when 4 then 23 # 8 MB (good)
176
+ when 5 then 24 # 16 MB (best)
177
+ else 22 # default: 4 MB
178
+ end
179
+
180
+ # RAR5 uses specific LZMA parameters: lc=1, lp=2, pb=0
181
+ # (Different from standalone LZMA which typically uses lc=3, lp=0, pb=2)
182
+ LzmaOptions.new(level, dict_size, lc: 1, lp: 2, pb: 0)
183
+ end
184
+
185
+ # Simple options class for LZMA parameters
186
+ class LzmaOptions
187
+ attr_reader :level, :dict_size, :lc, :lp, :pb
188
+
189
+ def initialize(level, dict_size, lc: 3, lp: 0, pb: 2)
190
+ @level = level
191
+ @dict_size = dict_size
192
+ @lc = lc
193
+ @lp = lp
194
+ @pb = pb
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end