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,315 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+ require_relative "file_collector"
5
+ require_relative "stream_compressor"
6
+ require_relative "header_writer"
7
+ require_relative "models/file_entry"
8
+ require_relative "../../models/split_options"
9
+
10
+ module Omnizip
11
+ module Formats
12
+ module SevenZip
13
+ # Split archive writer for .7z format
14
+ # Creates multi-volume archives with size limits
15
+ class SplitArchiveWriter
16
+ include Constants
17
+
18
+ attr_reader :base_path, :options, :split_options, :entries, :volumes
19
+
20
+ # Volume information
21
+ class VolumeInfo
22
+ attr_accessor :path, :size, :start_offset, :end_offset
23
+
24
+ def initialize(path, start_offset = 0)
25
+ @path = path
26
+ @size = 0
27
+ @start_offset = start_offset
28
+ @end_offset = start_offset
29
+ end
30
+ end
31
+
32
+ # Initialize writer
33
+ #
34
+ # @param base_path [String] Base path (e.g., "backup.7z.001")
35
+ # @param options [Hash] Compression options
36
+ # @param split_options [Models::SplitOptions] Split configuration
37
+ def initialize(base_path, options = {}, split_options = nil)
38
+ @base_path = base_path
39
+ @options = {
40
+ algorithm: :lzma2,
41
+ level: 5,
42
+ solid: true,
43
+ filters: [],
44
+ }.merge(options)
45
+ @split_options = split_options || Models::SplitOptions.new
46
+ @split_options.validate!
47
+ @collector = FileCollector.new
48
+ @entries = []
49
+ @volumes = []
50
+ @current_volume = nil
51
+ @current_volume_number = 1
52
+ @global_offset = 0
53
+ end
54
+
55
+ # Add file to archive
56
+ #
57
+ # @param file_path [String] Path to file
58
+ # @param archive_path [String, nil] Path in archive
59
+ def add_file(file_path, archive_path = nil)
60
+ @collector.add_path(file_path, archive_path: archive_path,
61
+ recursive: false)
62
+ end
63
+
64
+ # Add directory to archive
65
+ #
66
+ # @param dir_path [String] Path to directory
67
+ # @param recursive [Boolean] Add contents recursively
68
+ def add_directory(dir_path, recursive: true)
69
+ @collector.add_path(dir_path, recursive: recursive)
70
+ end
71
+
72
+ # Add files matching glob pattern
73
+ #
74
+ # @param pattern [String] Glob pattern
75
+ def add_files(pattern)
76
+ @collector.add_glob(pattern)
77
+ end
78
+
79
+ # Write split archive
80
+ #
81
+ # @raise [RuntimeError] on write error
82
+ def write
83
+ # Collect files
84
+ @entries = @collector.collect_files
85
+
86
+ # Determine spanning strategy
87
+ if @split_options.span_strategy == Omnizip::Models::SplitOptions::STRATEGY_BALANCED
88
+ write_balanced
89
+ else
90
+ write_first_fit
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ # Write using first-fit strategy (default)
97
+ def write_first_fit
98
+ # Compress all files first
99
+ compressed_result = compress_files
100
+
101
+ # Calculate total size needed
102
+ compressed_result[:data].bytesize
103
+ header_data = build_next_header(compressed_result)
104
+ header_data.bytesize
105
+
106
+ # Create volumes and write data
107
+ start_first_volume
108
+ write_packed_data(compressed_result[:data])
109
+
110
+ # Write header at the end of last volume
111
+ header_offset = @global_offset - START_HEADER_SIZE
112
+ write_data(header_data)
113
+
114
+ # Write start header to first volume (this closes volumes)
115
+ write_start_header_to_first_volume(header_data, header_offset)
116
+ end
117
+
118
+ # Write using balanced strategy
119
+ # Pre-calculates optimal file distribution
120
+ def write_balanced
121
+ # For now, use first-fit strategy
122
+ # Full balanced implementation would require:
123
+ # 1. Calculate individual file sizes
124
+ # 2. Use bin-packing algorithm
125
+ # 3. Distribute files optimally
126
+ write_first_fit
127
+ end
128
+
129
+ # Compress all files
130
+ #
131
+ # @return [Hash] Compression results
132
+ def compress_files
133
+ compressor = StreamCompressor.new(
134
+ algorithm: @options[:algorithm],
135
+ level: @options[:level],
136
+ filters: @options[:filters],
137
+ )
138
+
139
+ files_with_data = @entries.select(&:has_stream?)
140
+
141
+ if @options[:solid]
142
+ # Solid: compress all files into one stream
143
+ result = compressor.compress_files(files_with_data)
144
+ files_with_data.each_with_index do |entry, i|
145
+ entry.crc = result[:crcs][i]
146
+ entry.size = result[:unpack_sizes][i]
147
+ end
148
+
149
+ {
150
+ data: result[:packed_data],
151
+ folders: [{
152
+ method_id: compressor.method_id,
153
+ properties: compressor.properties,
154
+ unpack_size: result[:unpack_size],
155
+ }],
156
+ pack_sizes: [result[:packed_size]],
157
+ unpack_sizes: result[:unpack_sizes],
158
+ digests: result[:crcs],
159
+ }
160
+ else
161
+ # Non-solid: compress each file separately
162
+ packed_data = String.new(encoding: "BINARY")
163
+ folders = []
164
+ pack_sizes = []
165
+ unpack_sizes = []
166
+ digests = []
167
+
168
+ files_with_data.each do |entry|
169
+ data = File.binread(entry.source_path)
170
+ compressed = compressor.compress(data)
171
+
172
+ packed_data << compressed
173
+ pack_sizes << compressed.bytesize
174
+ unpack_sizes << data.bytesize
175
+
176
+ # Calculate CRC
177
+ crc = Omnizip::Checksums::Crc32.new
178
+ crc.update(data)
179
+ entry.crc = crc.value
180
+ digests << crc.value
181
+
182
+ folders << {
183
+ method_id: compressor.method_id,
184
+ properties: compressor.properties,
185
+ unpack_size: data.bytesize,
186
+ }
187
+ end
188
+
189
+ {
190
+ data: packed_data,
191
+ folders: folders,
192
+ pack_sizes: pack_sizes,
193
+ unpack_sizes: unpack_sizes,
194
+ digests: digests,
195
+ }
196
+ end
197
+ end
198
+
199
+ # Build next header metadata
200
+ #
201
+ # @param compressed_result [Hash] Compression results
202
+ # @return [String] Encoded next header
203
+ def build_next_header(compressed_result)
204
+ header_writer = HeaderWriter.new
205
+
206
+ header_options = {
207
+ streams: {
208
+ pack_pos: 0,
209
+ pack_sizes: compressed_result[:pack_sizes],
210
+ pack_crcs: [],
211
+ folders: compressed_result[:folders],
212
+ unpack_sizes: compressed_result[:unpack_sizes],
213
+ digests: compressed_result[:digests],
214
+ },
215
+ entries: @entries,
216
+ }
217
+
218
+ header_writer.write_next_header(header_options)
219
+ end
220
+
221
+ # Start first volume
222
+ def start_first_volume
223
+ volume_path = @split_options.volume_filename(@base_path, 1)
224
+ @current_volume = File.open(volume_path, "wb")
225
+ @volumes << VolumeInfo.new(volume_path, 0)
226
+
227
+ # Reserve space for start header (will be written at end)
228
+ @current_volume.write("\0" * START_HEADER_SIZE)
229
+ @global_offset = START_HEADER_SIZE
230
+ @volumes.last.size = START_HEADER_SIZE
231
+ @volumes.last.end_offset = START_HEADER_SIZE
232
+ end
233
+
234
+ # Write packed data across volumes
235
+ #
236
+ # @param data [String] Data to write
237
+ def write_packed_data(data)
238
+ offset = 0
239
+ remaining = data.bytesize
240
+
241
+ while remaining.positive?
242
+ available = @split_options.volume_size - @volumes.last.size
243
+
244
+ # If current volume is full, start a new one
245
+ if available <= 0
246
+ close_current_volume
247
+ start_continuation_volume
248
+ available = @split_options.volume_size
249
+ end
250
+
251
+ chunk_size = [available, remaining].min
252
+
253
+ if chunk_size.positive?
254
+ chunk = data[offset, chunk_size]
255
+ @current_volume.write(chunk)
256
+ @volumes.last.size += chunk_size
257
+ @volumes.last.end_offset += chunk_size
258
+ @global_offset += chunk_size
259
+ offset += chunk_size
260
+ remaining -= chunk_size
261
+ end
262
+ end
263
+ end
264
+
265
+ # Write data (may span volumes)
266
+ #
267
+ # @param data [String] Data to write
268
+ def write_data(data)
269
+ write_packed_data(data)
270
+ end
271
+
272
+ # Start continuation volume
273
+ def start_continuation_volume
274
+ @current_volume_number += 1
275
+ volume_path = @split_options.volume_filename(@base_path,
276
+ @current_volume_number)
277
+ @current_volume = File.open(volume_path, "wb")
278
+ @volumes << VolumeInfo.new(volume_path, @global_offset)
279
+ end
280
+
281
+ # Write start header to first volume
282
+ #
283
+ # @param header_data [String] Header data
284
+ # @param header_offset [Integer] Offset to header
285
+ def write_start_header_to_first_volume(header_data, header_offset)
286
+ # Close current volume first to ensure all data is flushed
287
+ close_current_volume
288
+
289
+ header_writer = HeaderWriter.new
290
+ start_header = header_writer.write_start_header(
291
+ header_data,
292
+ header_offset,
293
+ )
294
+
295
+ # Open first volume and write start header
296
+ first_volume_path = @volumes.first.path
297
+ File.open(first_volume_path, "r+b") do |io|
298
+ io.seek(0)
299
+ io.write(start_header)
300
+ io.flush
301
+ end
302
+ end
303
+
304
+ # Close current volume
305
+ def close_current_volume
306
+ return unless @current_volume
307
+
308
+ @current_volume.flush
309
+ @current_volume.close
310
+ @current_volume = nil
311
+ end
312
+ end
313
+ end
314
+ end
315
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+ require_relative "../../checksums/crc32"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module SevenZip
9
+ # Compresses data streams using coder chains
10
+ # Opposite of StreamDecompressor - applies compression algorithms
11
+ class StreamCompressor
12
+ attr_reader :algorithm, :level, :filters
13
+
14
+ # Initialize compressor
15
+ #
16
+ # @param algorithm [Symbol] Compression algorithm
17
+ # @param level [Integer] Compression level (1-9)
18
+ # @param filters [Array<Symbol>] Filter chain
19
+ def initialize(algorithm: :lzma2, level: 5, filters: [])
20
+ @algorithm = algorithm
21
+ @level = level
22
+ @filters = Array(filters)
23
+ end
24
+
25
+ # Compress data
26
+ #
27
+ # @param data [String] Uncompressed data
28
+ # @return [String] Compressed data
29
+ def compress(data)
30
+ result = data
31
+
32
+ # Apply filters first
33
+ @filters.each do |filter_sym|
34
+ filter_class = FilterRegistry.get(filter_sym)
35
+ next unless filter_class
36
+
37
+ filter = filter_class.new
38
+ input_io = StringIO.new(result)
39
+ output_io = StringIO.new
40
+ output_io.set_encoding("BINARY")
41
+ filter.encode(input_io, output_io)
42
+ result = output_io.string
43
+ end
44
+
45
+ # Apply compression algorithm
46
+ if @algorithm && @algorithm != :copy
47
+ algo_class = AlgorithmRegistry.get(@algorithm)
48
+ raise "Algorithm not found: #{@algorithm}" unless algo_class
49
+
50
+ encoder = algo_class.new
51
+ input_io = StringIO.new(result)
52
+ output_io = StringIO.new
53
+ output_io.set_encoding("BINARY")
54
+
55
+ # For 7-Zip format, use raw_mode (no property byte in compressed data)
56
+ # The properties are encoded in the 7-Zip header instead
57
+ encoder.compress(input_io, output_io, { raw_mode: true, standalone: false })
58
+ result = output_io.string
59
+ end
60
+
61
+ result
62
+ end
63
+
64
+ # Compress multiple files into single stream (solid compression)
65
+ #
66
+ # @param file_entries [Array<Models::FileEntry>] Files to compress
67
+ # @return [Hash] Compression result with packed/unpacked sizes
68
+ def compress_files(file_entries)
69
+ # Concatenate all file data
70
+ combined_data = String.new(encoding: "BINARY")
71
+ unpack_sizes = []
72
+ crcs = []
73
+
74
+ file_entries.each do |entry|
75
+ next unless entry.has_stream? && entry.source_path
76
+
77
+ data = File.binread(entry.source_path)
78
+ combined_data << data
79
+ unpack_sizes << data.bytesize
80
+
81
+ # Calculate CRC
82
+ crc = Omnizip::Checksums::Crc32.new
83
+ crc.update(data)
84
+ crcs << crc.value
85
+ end
86
+
87
+ # Compress combined data
88
+ packed_data = compress(combined_data)
89
+
90
+ {
91
+ packed_data: packed_data,
92
+ packed_size: packed_data.bytesize,
93
+ unpack_size: combined_data.bytesize,
94
+ unpack_sizes: unpack_sizes,
95
+ crcs: crcs,
96
+ }
97
+ end
98
+
99
+ # Get method ID for this compression algorithm
100
+ #
101
+ # @return [Integer] Method ID
102
+ def method_id
103
+ case @algorithm
104
+ when :copy then Constants::MethodId::COPY
105
+ when :lzma then Constants::MethodId::LZMA
106
+ when :lzma2 then Constants::MethodId::LZMA2
107
+ when :ppmd, :ppmd7 then Constants::MethodId::PPMD
108
+ when :bzip2 then Constants::MethodId::BZIP2
109
+ else Constants::MethodId::LZMA2
110
+ end
111
+ end
112
+
113
+ # Get filter IDs for filter chain
114
+ #
115
+ # @return [Array<Integer>] Filter IDs
116
+ def filter_ids
117
+ @filters.filter_map do |filter_sym|
118
+ case filter_sym
119
+ when :bcj_x86 then Constants::FilterId::BCJ_X86
120
+ when :delta then Constants::FilterId::DELTA
121
+ end
122
+ end
123
+ end
124
+
125
+ # Get properties for compression algorithm
126
+ #
127
+ # @return [String, nil] Binary properties
128
+ def properties
129
+ return nil if @algorithm == :copy || @algorithm.nil?
130
+
131
+ case @algorithm
132
+ when :lzma2
133
+ # LZMA2 properties: dictionary size encoded
134
+ dict_size = 1 << (15 + @level)
135
+ prop = 0
136
+ prop += 1 while dict_size > (2 << prop)
137
+ [prop].pack("C")
138
+ when :lzma
139
+ # LZMA properties: lc, lp, pb, dict_size
140
+ lc = 3
141
+ lp = 0
142
+ pb = 2
143
+ dict_size = 1 << (15 + @level)
144
+ [lc + (lp * 9) + (pb * 9 * 5)].pack("C") +
145
+ [dict_size].pack("V")
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "coder_chain"
4
+ require_relative "../../checksums/crc32"
5
+ require "stringio"
6
+
7
+ module Omnizip
8
+ module Formats
9
+ module SevenZip
10
+ # Decompresses .7z streams using coder chains
11
+ # Handles packed stream extraction and CRC validation
12
+ class StreamDecompressor
13
+ attr_reader :archive_io, :folder, :chain_config
14
+
15
+ # Initialize decompressor
16
+ #
17
+ # @param archive_io [IO] Archive file handle
18
+ # @param folder [Models::Folder] Folder specification
19
+ # @param pack_pos [Integer] Position of packed data
20
+ # @param pack_size [Integer] Size of packed data
21
+ # @param header [Header, nil] Optional header for split archive handling
22
+ def initialize(archive_io, folder, pack_pos, pack_size, header = nil)
23
+ @archive_io = archive_io
24
+ @folder = folder
25
+ @pack_pos = pack_pos
26
+ @pack_size = pack_size
27
+ @header = header
28
+ @chain_config = CoderChain.build_from_folder(folder)
29
+ end
30
+
31
+ # Decompress stream to output
32
+ #
33
+ # @param size [Integer] Expected uncompressed size
34
+ # @return [String] Decompressed data
35
+ # @raise [RuntimeError] on decompression error
36
+ def decompress(size)
37
+ # Seek to packed data
38
+ @archive_io.seek(@pack_pos)
39
+
40
+ # For multi-volume archives, pack_size from stream_info may be incomplete.
41
+ # For solid archives spanning volumes, the actual compressed data extends
42
+ # from pack_pos to where the next header starts.
43
+ if @archive_io.is_a?(SplitArchiveReader::MultiVolumeIO) && @header
44
+ # next_header_offset is relative to start_pos_after_header
45
+ # Actual packed data size is from pack_pos to next header position
46
+ next_header_position = @header.start_pos_after_header + @header.next_header_offset
47
+ actual_pack_size = next_header_position - @pack_pos
48
+ packed_data = @archive_io.read(actual_pack_size)
49
+ else
50
+ # Regular single-file archive: use pack_size from stream info
51
+ packed_data = @archive_io.read(@pack_size)
52
+ end
53
+
54
+ return packed_data if @chain_config.nil? # No compression
55
+
56
+ # Get algorithm
57
+ algo_sym = @chain_config[:algorithm]
58
+ return packed_data unless algo_sym # Copy method
59
+
60
+ # Get algorithm class
61
+ algo_class = AlgorithmRegistry.get(algo_sym)
62
+ raise "Algorithm not found: #{algo_sym}" unless algo_class
63
+
64
+ # Decompress
65
+ input_io = StringIO.new(packed_data)
66
+ output_io = StringIO.new
67
+ output_io.set_encoding("BINARY")
68
+
69
+ # Build options from coder properties for 7-Zip format
70
+ decoder_options = build_decoder_options
71
+
72
+ decoder = algo_class.new(decoder_options)
73
+ decoder.decompress(input_io, output_io, size: size)
74
+
75
+ result = output_io.string
76
+
77
+ # Apply filters if present
78
+ if @chain_config[:filters] && !@chain_config[:filters].empty?
79
+ @chain_config[:filters].reverse_each do |filter_sym|
80
+ filter_class = FilterRegistry.get(filter_sym)
81
+ next unless filter_class
82
+
83
+ filter = filter_class.new
84
+ filtered = StringIO.new
85
+ filter.reverse(StringIO.new(result), filtered)
86
+ result = filtered.string
87
+ end
88
+ end
89
+
90
+ result
91
+ end
92
+
93
+ # Decompress and verify CRC
94
+ #
95
+ # @param size [Integer] Expected uncompressed size
96
+ # @param expected_crc [Integer, nil] Expected CRC32 value
97
+ # @return [String] Decompressed data
98
+ # @raise [RuntimeError] if CRC mismatch
99
+ def decompress_and_verify(size, expected_crc = nil)
100
+ data = decompress(size)
101
+
102
+ if expected_crc
103
+ crc = Omnizip::Checksums::Crc32.new
104
+ crc.update(data)
105
+ actual_crc = crc.value
106
+
107
+ unless actual_crc == expected_crc
108
+ raise "CRC mismatch: expected 0x#{expected_crc.to_s(16)}, " \
109
+ "got 0x#{actual_crc.to_s(16)}"
110
+ end
111
+ end
112
+
113
+ data
114
+ end
115
+
116
+ private
117
+
118
+ # Build decoder options from coder properties
119
+ #
120
+ # For 7-Zip format, coder properties contain algorithm-specific data.
121
+ # For LZMA2, it's a single byte encoding the dictionary size.
122
+ #
123
+ # @return [Hash] Decoder options
124
+ def build_decoder_options
125
+ return {} unless @chain_config
126
+
127
+ options = {}
128
+ properties = @chain_config[:properties]
129
+
130
+ if properties && !properties.empty?
131
+ algo_sym = @chain_config[:algorithm]
132
+
133
+ case algo_sym
134
+ when :lzma2
135
+ # LZMA2 properties: single byte encoding dictionary size
136
+ prop_byte = properties.getbyte(0)
137
+ dict_size = Omnizip::Algorithms::LZMA2::Properties.decode_dict_size(prop_byte)
138
+ options[:raw_mode] = true # No property byte in data stream
139
+ options[:dict_size] = dict_size
140
+ when :lzma
141
+ # LZMA properties: 5 bytes (prop byte + dict size)
142
+ # Format: 1 byte (lc/lp/pb) + 4 bytes (dict size LE)
143
+ if properties.bytesize >= 5
144
+ props_byte = properties.getbyte(0)
145
+ dict_size = properties[1, 4].unpack1("V")
146
+ # Use lzma2_mode to skip header reading - 7-Zip provides properties separately
147
+ options[:lzma2_mode] = true
148
+ options[:lc] = props_byte % 9
149
+ remainder = props_byte / 9
150
+ options[:lp] = remainder % 5
151
+ options[:pb] = remainder / 5
152
+ options[:dict_size] = dict_size
153
+ end
154
+ end
155
+ end
156
+
157
+ options
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end