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,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "galois16"
4
+ require_relative "par2cmdline_algorithm"
5
+
6
+ module Omnizip
7
+ module Parity
8
+ # Reed-Solomon matrix for incremental chunk processing
9
+ #
10
+ # This class computes the RS matrix coefficients ONCE using Gaussian
11
+ # elimination, then provides methods to apply those coefficients
12
+ # incrementally to data chunks.
13
+ #
14
+ # Based on par2cmdline's approach (reedsolomon.h, par2repairer.cpp)
15
+ class ReedSolomonMatrix
16
+ # @return [Array<Integer>] Indices of present input blocks
17
+ attr_reader :present_indices
18
+
19
+ # @return [Array<Integer>] Indices of missing input blocks
20
+ attr_reader :missing_indices
21
+
22
+ # @return [Array<Integer>] All available recovery exponents
23
+ attr_reader :recovery_exponents
24
+
25
+ # @return [Array<Integer>] Recovery exponents actually used (first num_missing)
26
+ attr_reader :used_recovery_exponents
27
+
28
+ # @return [Integer] Total number of input blocks
29
+ attr_reader :total_inputs
30
+
31
+ # @return [Integer] Block size in bytes
32
+ attr_reader :block_size
33
+
34
+ # @return [Array<Array<Integer>>] Solved matrix coefficients (num_missing x num_missing)
35
+ attr_reader :matrix
36
+
37
+ # @return [Array<Integer>] Selected bases for Galois field
38
+ attr_reader :bases
39
+
40
+ # Initialize matrix
41
+ #
42
+ # @param present_indices [Array<Integer>] Indices of available data blocks
43
+ # @param missing_indices [Array<Integer>] Indices of blocks to recover
44
+ # @param recovery_exponents [Array<Integer>] Exponents of recovery blocks to use
45
+ # @param total_inputs [Integer] Total number of input blocks (present + missing)
46
+ # @param block_size [Integer] Block size in bytes
47
+ def initialize(present_indices, missing_indices, recovery_exponents,
48
+ total_inputs, block_size)
49
+ @present_indices = present_indices.sort
50
+ @missing_indices = missing_indices.sort
51
+ @recovery_exponents = recovery_exponents.sort
52
+ @total_inputs = total_inputs
53
+ @block_size = block_size
54
+ @matrix = nil # Computed by compute!
55
+ @bases = nil # Computed by compute!
56
+ @used_recovery_exponents = nil # Computed by compute!
57
+ end
58
+
59
+ # Compute matrix coefficients using Gaussian elimination
60
+ #
61
+ # CORRECT FORMULATION:
62
+ # We solve: A * x = b
63
+ # where:
64
+ # - A[i,j] = base[missing[j]]^exponent[i] (num_missing x num_missing)
65
+ # - x[j] = missing_block[j] (what we solve for)
66
+ # - b[i] = recovery[i] - sum(present[k] * base[present[k]]^exponent[i])
67
+ #
68
+ # This method computes A^-1, so we can later compute:
69
+ # x = A^-1 * b
70
+ #
71
+ # @return [void]
72
+ def compute!
73
+ @bases = Par2cmdlineAlgorithm.compute_bases(total_inputs)
74
+
75
+ num_missing = missing_indices.size
76
+
77
+ # Select which recovery exponents to use (first num_missing)
78
+ @used_recovery_exponents = recovery_exponents.first(num_missing)
79
+
80
+ # Build A matrix: A[i,j] = base[missing[j]]^exponent[i]
81
+ # This is num_missing x num_missing (SQUARE)
82
+ a_matrix = Array.new(num_missing) { Array.new(num_missing, 0) }
83
+
84
+ @used_recovery_exponents.each_with_index do |exponent, row|
85
+ missing_indices.each_with_index do |idx, col|
86
+ a_matrix[row][col] = Galois16.power(@bases[idx], exponent)
87
+ end
88
+ end
89
+
90
+ # Invert A matrix using Gaussian elimination
91
+ # Create augmented matrix [A | I]
92
+ identity = Array.new(num_missing) do |i|
93
+ Array.new(num_missing) do |j|
94
+ i == j ? 1 : 0
95
+ end
96
+ end
97
+
98
+ gaussian_elimination!(a_matrix, identity)
99
+
100
+ # Store inverted matrix (now in identity position)
101
+ # IMPORTANT: Transpose it so rows=missing_indices, cols=recovery_indices
102
+ # This allows direct indexing: matrix[output_idx][recovery_idx]
103
+ @matrix = identity.transpose
104
+ end
105
+
106
+ # Get matrix coefficient for computing missing blocks
107
+ #
108
+ # This returns the coefficient from A^-1 matrix that tells us how much
109
+ # each recovery block contributes to each missing block.
110
+ #
111
+ # After transposition, matrix structure is:
112
+ # - matrix[recovery_idx][output_idx] (rows=recovery, cols=missing)
113
+ #
114
+ # For x = A^-1 * b:
115
+ # x[output_idx] = sum over recovery_idx of A^-1[output_idx][recovery_idx] * b[recovery_idx]
116
+ #
117
+ # Due to transpose, we access as: matrix[recovery_idx][output_idx]
118
+ #
119
+ # @param output_idx [Integer] Output block index (0..missing_count-1)
120
+ # @param recovery_idx [Integer] Recovery block index (0..recovery_count-1)
121
+ # @return [Integer] Galois field coefficient
122
+ def coefficient(output_idx, recovery_idx)
123
+ raise "Matrix not computed - call compute! first" unless @matrix
124
+
125
+ # After transpose, indices are swapped: @matrix[recovery_idx][output_idx]
126
+ @matrix[recovery_idx][output_idx]
127
+ end
128
+
129
+ # Get coefficient for present block contribution to b vector
130
+ #
131
+ # Returns how much a present block contributes when building b vector.
132
+ # This is: -base[present_idx]^exponent[recovery_idx]
133
+ #
134
+ # @param present_idx [Integer] Index of present data block
135
+ # @param recovery_exponent [Integer] Recovery block exponent
136
+ # @return [Integer] Galois field coefficient
137
+ def present_contribution_coefficient(present_idx, recovery_exponent)
138
+ Galois16.power(@bases[present_idx], recovery_exponent)
139
+ end
140
+
141
+ # Process a chunk of data: output_chunk ^= input_chunk * factor
142
+ #
143
+ # This is called thousands of times during repair to incrementally
144
+ # build up each recovered block chunk by chunk.
145
+ #
146
+ # @param factor [Integer] Galois field multiplier (from matrix)
147
+ # @param input_chunk [String] Input chunk data
148
+ # @param output_block [String] Full output block (modified in place)
149
+ # @param chunk_size [Integer] Chunk size in bytes (must be even)
150
+ # @param output_offset [Integer] Offset within output block where to write
151
+ def process_chunk(factor, input_chunk, output_block, chunk_size,
152
+ output_offset: 0)
153
+ return if factor.zero? # Optimization
154
+
155
+ num_words = chunk_size / 2
156
+
157
+ num_words.times do |i|
158
+ input_offset = i * 2
159
+ block_offset = output_offset + (i * 2)
160
+
161
+ # Read 16-bit words (little-endian)
162
+ input_word = input_chunk.getbyte(input_offset) |
163
+ (input_chunk.getbyte(input_offset + 1) << 8)
164
+
165
+ output_word = output_block.getbyte(block_offset) |
166
+ (output_block.getbyte(block_offset + 1) << 8)
167
+
168
+ # Galois multiplication and addition (XOR)
169
+ result = Galois16.add(output_word,
170
+ Galois16.multiply(input_word, factor))
171
+
172
+ # Write back as bytes (little-endian)
173
+ output_block.setbyte(block_offset, result & 0xFF)
174
+ output_block.setbyte(block_offset + 1, (result >> 8) & 0xFF)
175
+ end
176
+ end
177
+
178
+ # Get number of recovery blocks used
179
+ #
180
+ # @return [Integer] Recovery count
181
+ def recovery_count
182
+ recovery_exponents.size
183
+ end
184
+
185
+ # Get number of output blocks (missing)
186
+ #
187
+ # @return [Integer] Output count
188
+ def output_count
189
+ missing_indices.size
190
+ end
191
+
192
+ private
193
+
194
+ # Perform Gaussian elimination to invert matrix
195
+ #
196
+ # Transforms [A | I] into [I | A^-1]
197
+ #
198
+ # @param left_matrix [Array<Array<Integer>>] Matrix to invert (modified)
199
+ # @param right_matrix [Array<Array<Integer>>] Identity matrix (becomes inverse)
200
+ def gaussian_elimination!(left_matrix, right_matrix)
201
+ num_rows = left_matrix.size
202
+ num_cols = left_matrix[0].size
203
+
204
+ num_rows.times do |pivot_row|
205
+ pivot = left_matrix[pivot_row][pivot_row]
206
+ raise "Singular matrix at row #{pivot_row}" if pivot.zero?
207
+
208
+ # Scale pivot row to make pivot = 1
209
+ unless pivot == 1
210
+ num_cols.times do |col|
211
+ next if left_matrix[pivot_row][col].zero?
212
+
213
+ left_matrix[pivot_row][col] =
214
+ Galois16.divide(left_matrix[pivot_row][col], pivot)
215
+ end
216
+
217
+ num_cols.times do |col|
218
+ next if right_matrix[pivot_row][col].zero?
219
+
220
+ right_matrix[pivot_row][col] =
221
+ Galois16.divide(right_matrix[pivot_row][col], pivot)
222
+ end
223
+ end
224
+
225
+ # Eliminate column in all other rows
226
+ num_rows.times do |row|
227
+ next if row == pivot_row
228
+
229
+ scale = left_matrix[row][pivot_row]
230
+ next if scale.zero?
231
+
232
+ if scale == 1
233
+ num_cols.times do |col|
234
+ next if left_matrix[pivot_row][col].zero?
235
+
236
+ left_matrix[row][col] = Galois16.add(
237
+ left_matrix[row][col],
238
+ left_matrix[pivot_row][col],
239
+ )
240
+ end
241
+
242
+ num_cols.times do |col|
243
+ next if right_matrix[pivot_row][col].zero?
244
+
245
+ right_matrix[row][col] = Galois16.add(
246
+ right_matrix[row][col],
247
+ right_matrix[pivot_row][col],
248
+ )
249
+ end
250
+ else
251
+ num_cols.times do |col|
252
+ next if left_matrix[pivot_row][col].zero?
253
+
254
+ scaled = Galois16.multiply(left_matrix[pivot_row][col], scale)
255
+ left_matrix[row][col] =
256
+ Galois16.add(left_matrix[row][col], scaled)
257
+ end
258
+
259
+ num_cols.times do |col|
260
+ next if right_matrix[pivot_row][col].zero?
261
+
262
+ scaled = Galois16.multiply(right_matrix[pivot_row][col], scale)
263
+ right_matrix[row][col] =
264
+ Galois16.add(right_matrix[row][col], scaled)
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ # Verify that A * A^-1 = Identity
272
+ # @param a_original [Array<Array<Integer>>] Original A matrix
273
+ # @param a_inv [Array<Array<Integer>>] Computed A^-1 matrix
274
+ # @return [Boolean] true if verification passes
275
+ def verify_matrix_inversion(a_original, a_inv)
276
+ n = a_original.size
277
+
278
+ # Compute A * A^-1
279
+ result = Array.new(n) { Array.new(n, 0) }
280
+ n.times do |i|
281
+ n.times do |j|
282
+ sum = 0
283
+ n.times do |k|
284
+ product = Galois16.multiply(a_original[i][k], a_inv[k][j])
285
+ sum = Galois16.add(sum, product)
286
+ end
287
+ result[i][j] = sum
288
+ end
289
+ end
290
+
291
+ # Check if result is identity
292
+ n.times do |i|
293
+ n.times do |j|
294
+ expected = i == j ? 1 : 0
295
+ return false if result[i][j] != expected
296
+ end
297
+ end
298
+
299
+ true
300
+ end
301
+ end
302
+ end
303
+
304
+ # Verify that A * A^-1 = Identity
305
+ #
306
+ # @param a_original [Array<Array<Integer>>] Original A matrix
307
+ # @param a_inv [Array<Array<Integer>>] Computed A^-1 matrix
308
+ # @return [Boolean] true if verification passes
309
+ def self.verify_matrix_inversion(a_original, a_inv)
310
+ n = a_original.size
311
+
312
+ # Compute A * A^-1
313
+ result = Array.new(n) { Array.new(n, 0) }
314
+ n.times do |i|
315
+ n.times do |j|
316
+ sum = 0
317
+ n.times do |k|
318
+ product = Galois16.multiply(a_original[i][k], a_inv[k][j])
319
+ sum = Galois16.add(sum, product)
320
+ end
321
+ result[i][j] = sum
322
+ end
323
+ end
324
+
325
+ # Check if result is identity
326
+ n.times do |i|
327
+ n.times do |j|
328
+ expected = i == j ? 1 : 0
329
+ if result[i][j] != expected
330
+ warn "MATRIX VERIFICATION FAILED!"
331
+ warn " A * A^-1 at [#{i},#{j}] = 0x#{format('%04X',
332
+ result[i][j])} (expected #{expected})"
333
+ warn " This means Gaussian elimination produced wrong inverse!"
334
+ return false
335
+ end
336
+ end
337
+ end
338
+
339
+ warn "Matrix verification: A * A^-1 = I ✓"
340
+ true
341
+ end
342
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parity/par2cmdline_coefficients"
4
+ require_relative "parity/par2_creator"
5
+ require_relative "parity/par2_verifier"
6
+ require_relative "parity/par2_repairer"
7
+
8
+ module Omnizip
9
+ # PAR2 parity archive support
10
+ #
11
+ # Provides creation, verification, and repair of PAR2 parity files
12
+ # for protecting archives and data files against corruption.
13
+ #
14
+ # PAR2 uses Reed-Solomon error correction codes to create recovery
15
+ # data that can reconstruct missing or corrupted blocks.
16
+ #
17
+ # @example Create PAR2 protection
18
+ # Omnizip::Parity.create('archive.zip', redundancy: 10)
19
+ #
20
+ # @example Verify and repair
21
+ # result = Omnizip::Parity.verify('archive.par2')
22
+ # Omnizip::Parity.repair('archive.par2') if result.repairable?
23
+ module Parity
24
+ class << self
25
+ # Create PAR2 recovery files for archive or files
26
+ #
27
+ # @param file_or_pattern [String] File path or glob pattern
28
+ # @param redundancy [Integer] Redundancy percentage (0-100)
29
+ # @param block_size [Integer] Block size in bytes
30
+ # @param output_dir [String, nil] Output directory for PAR2 files
31
+ # @param progress [Proc, nil] Progress callback
32
+ # @return [Array<String>] Created PAR2 file paths
33
+ #
34
+ # @example Create with 10% redundancy
35
+ # Omnizip::Parity.create('backup.7z', redundancy: 10)
36
+ #
37
+ # @example Create for multiple files
38
+ # Omnizip::Parity.create('data/*.dat', redundancy: 5)
39
+ #
40
+ # @example With progress tracking
41
+ # Omnizip::Parity.create('large.zip',
42
+ # redundancy: 10,
43
+ # progress: ->(pct, msg) { puts "#{pct}%: #{msg}" }
44
+ # )
45
+ def create(file_or_pattern, redundancy: 5, block_size: Par2Creator::DEFAULT_BLOCK_SIZE,
46
+ output_dir: nil, progress: nil)
47
+ # Try glob expansion first (handles both patterns and single files)
48
+ files = Dir.glob(file_or_pattern)
49
+
50
+ # If glob returns nothing, check if it's a single existing file
51
+ if files.empty? && File.exist?(file_or_pattern) && !File.directory?(file_or_pattern)
52
+ files = [file_or_pattern]
53
+ end
54
+
55
+ if files.empty?
56
+ raise ArgumentError,
57
+ "No files match pattern: #{file_or_pattern}"
58
+ end
59
+
60
+ # Create PAR2 creator
61
+ creator = Par2Creator.new(
62
+ redundancy: redundancy,
63
+ block_size: block_size,
64
+ progress: progress,
65
+ )
66
+
67
+ # Add all files
68
+ files.each { |file| creator.add_file(file) }
69
+
70
+ # Determine output base name
71
+ dir = output_dir || File.dirname(files.first)
72
+ base_name = if files.size == 1
73
+ # Use file's directory and base name without extension
74
+ File.join(dir, File.basename(files.first, ".*"))
75
+ else
76
+ # Use files' directory name for multiple files
77
+ File.join(dir,
78
+ File.basename(File.dirname(files.first)))
79
+ end
80
+
81
+ # Create PAR2 files
82
+ creator.create(base_name)
83
+ end
84
+
85
+ # Verify files using PAR2 recovery data
86
+ #
87
+ # @param par2_file [String] Path to .par2 index file
88
+ # @return [Par2Verifier::VerificationResult] Verification results
89
+ #
90
+ # @example Verify archive integrity
91
+ # result = Omnizip::Parity.verify('backup.par2')
92
+ # if result.all_ok?
93
+ # puts "All files intact"
94
+ # elsif result.repairable?
95
+ # puts "Damage detected but repairable"
96
+ # else
97
+ # puts "Damage cannot be repaired"
98
+ # end
99
+ def verify(par2_file)
100
+ verifier = Par2Verifier.new(par2_file)
101
+ verifier.verify
102
+ end
103
+
104
+ # Repair damaged files using PAR2 recovery data
105
+ #
106
+ # @param par2_file [String] Path to .par2 index file
107
+ # @param output_dir [String, nil] Output directory for repaired files
108
+ # @param progress [Proc, nil] Progress callback
109
+ # @return [Par2Repairer::RepairResult] Repair results
110
+ #
111
+ # @example Repair damaged archive
112
+ # result = Omnizip::Parity.repair('backup.par2')
113
+ # if result.success?
114
+ # puts "Successfully recovered #{result.recovered_files.join(', ')}"
115
+ # else
116
+ # puts "Repair failed: #{result.error_message}"
117
+ # end
118
+ def repair(par2_file, output_dir: nil, progress: nil)
119
+ repairer = Par2Repairer.new(par2_file, progress: progress)
120
+ repairer.repair(output_dir: output_dir)
121
+ end
122
+
123
+ # Quick check if PAR2 files exist for a file
124
+ #
125
+ # @param file_path [String] Path to protected file
126
+ # @return [Boolean] true if PAR2 files exist
127
+ #
128
+ # @example Check for protection
129
+ # if Omnizip::Parity.protected?('backup.zip')
130
+ # puts "File is protected by PAR2"
131
+ # end
132
+ def protected?(file_path)
133
+ base_name = File.basename(file_path, ".*")
134
+ dir_name = File.dirname(file_path)
135
+ par2_file = File.join(dir_name, "#{base_name}.par2")
136
+
137
+ File.exist?(par2_file)
138
+ end
139
+
140
+ # Get PAR2 protection information
141
+ #
142
+ # @param file_path [String] Path to protected file
143
+ # @return [Hash, nil] Protection information or nil if not protected
144
+ #
145
+ # @example Get protection info
146
+ # info = Omnizip::Parity.info('backup.zip')
147
+ # puts "Redundancy: #{info[:redundancy]}%"
148
+ # puts "Block size: #{info[:block_size]} bytes"
149
+ def info(file_path)
150
+ base_name = File.basename(file_path, ".*")
151
+ dir_name = File.dirname(file_path)
152
+ par2_file = File.join(dir_name, "#{base_name}.par2")
153
+
154
+ return nil unless File.exist?(par2_file)
155
+
156
+ verifier = Par2Verifier.new(par2_file)
157
+ verifier.send(:parse_par2_file)
158
+
159
+ total_blocks = verifier.send(:calculate_total_blocks)
160
+ recovery_blocks = verifier.instance_variable_get(:@recovery_blocks).size
161
+
162
+ {
163
+ par2_file: par2_file,
164
+ block_size: verifier.metadata[:block_size],
165
+ total_blocks: total_blocks,
166
+ file_count: verifier.metadata[:file_count] || verifier.instance_variable_get(:@file_list).size,
167
+ recovery_blocks: recovery_blocks,
168
+ redundancy: calculate_redundancy(total_blocks, recovery_blocks),
169
+ }
170
+ end
171
+
172
+ private
173
+
174
+ # Calculate redundancy percentage
175
+ #
176
+ # @param total_blocks [Integer] Total data blocks
177
+ # @param recovery_blocks [Integer] Recovery blocks
178
+ # @return [Float] Redundancy percentage
179
+ def calculate_redundancy(total_blocks, recovery_blocks)
180
+ return 0.0 if total_blocks.nil? || total_blocks.zero?
181
+
182
+ (recovery_blocks.to_f / total_blocks * 100).round(2)
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module Password
5
+ # Registry for encryption strategies
6
+ class EncryptionRegistry
7
+ @strategies = {}
8
+
9
+ class << self
10
+ # Register an encryption strategy
11
+ # @param name [Symbol] Strategy name
12
+ # @param strategy_class [Class] Strategy class
13
+ def register(name, strategy_class)
14
+ @strategies[name] = strategy_class
15
+ end
16
+
17
+ # Get a strategy by name
18
+ # @param name [Symbol] Strategy name
19
+ # @return [Class] Strategy class
20
+ # @raise [ArgumentError] If strategy not found
21
+ def get(name)
22
+ strategy = @strategies[name]
23
+ return strategy if strategy
24
+
25
+ raise ArgumentError, "Unknown encryption strategy: #{name}. " \
26
+ "Available: #{@strategies.keys.join(', ')}"
27
+ end
28
+
29
+ # Check if strategy is registered
30
+ # @param name [Symbol] Strategy name
31
+ # @return [Boolean] True if registered
32
+ def registered?(name)
33
+ @strategies.key?(name)
34
+ end
35
+
36
+ # Get all registered strategy names
37
+ # @return [Array<Symbol>] Strategy names
38
+ def strategies
39
+ @strategies.keys
40
+ end
41
+
42
+ # Create a strategy instance
43
+ # @param name [Symbol] Strategy name
44
+ # @param password [String] Password
45
+ # @param options [Hash] Strategy options
46
+ # @return [EncryptionStrategy] Strategy instance
47
+ def create(name, password, **options)
48
+ strategy_class = get(name)
49
+ strategy_class.new(password, **options)
50
+ end
51
+
52
+ # Reset registry (for testing)
53
+ def reset
54
+ @strategies = {}
55
+ end
56
+ end
57
+ end
58
+
59
+ # Register built-in strategies
60
+ EncryptionRegistry.register(:traditional, ZipCryptoStrategy)
61
+ EncryptionRegistry.register(:zip_crypto, ZipCryptoStrategy)
62
+ EncryptionRegistry.register(:winzip_aes, WinzipAesStrategy)
63
+ EncryptionRegistry.register(:aes256, WinzipAesStrategy)
64
+ end
65
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module Password
5
+ # Base class for encryption strategies
6
+ # Defines the interface for encrypting/decrypting archive entries
7
+ class EncryptionStrategy
8
+ attr_reader :password
9
+
10
+ # Initialize encryption strategy
11
+ # @param password [String] Password to use
12
+ def initialize(password)
13
+ @password = password
14
+ validate_password
15
+ end
16
+
17
+ # Encrypt data
18
+ # @param data [String] Data to encrypt
19
+ # @return [String] Encrypted data
20
+ # @raise [NotImplementedError] Subclasses must implement
21
+ def encrypt(data)
22
+ raise NotImplementedError, "#{self.class} must implement #encrypt"
23
+ end
24
+
25
+ # Decrypt data
26
+ # @param data [String] Data to decrypt
27
+ # @return [String] Decrypted data
28
+ # @raise [NotImplementedError] Subclasses must implement
29
+ def decrypt(data)
30
+ raise NotImplementedError, "#{self.class} must implement #decrypt"
31
+ end
32
+
33
+ # Get encryption method ID for ZIP format
34
+ # @return [Integer] Compression method ID
35
+ # @raise [NotImplementedError] Subclasses must implement
36
+ def compression_method
37
+ raise NotImplementedError,
38
+ "#{self.class} must implement #compression_method"
39
+ end
40
+
41
+ # Get extra field data for ZIP header
42
+ # @return [String] Extra field data
43
+ def extra_field_data
44
+ ""
45
+ end
46
+
47
+ # Get encryption flags for ZIP header
48
+ # @return [Integer] Encryption flags
49
+ def encryption_flags
50
+ 0x0001 # Bit 0: encrypted
51
+ end
52
+
53
+ # Check if this strategy supports the given data
54
+ # @param data [String] Data to check
55
+ # @return [Boolean] True if supported
56
+ def supports?(_data)
57
+ true
58
+ end
59
+
60
+ # Get encryption method name
61
+ # @return [Symbol] Method name
62
+ def method_name
63
+ self.class.name.split("::").last
64
+ .gsub(/Strategy$/, "")
65
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
66
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
67
+ .downcase
68
+ .to_sym
69
+ end
70
+
71
+ protected
72
+
73
+ # Validate password
74
+ # @raise [ArgumentError] If password is invalid
75
+ def validate_password
76
+ raise ArgumentError, "Password cannot be nil" if password.nil?
77
+ raise ArgumentError, "Password cannot be empty" if password.empty?
78
+ end
79
+
80
+ # Derive encryption key from password
81
+ # @param salt [String] Salt for key derivation
82
+ # @param iterations [Integer] Number of iterations
83
+ # @return [String] Derived key
84
+ def derive_key(salt, iterations = 1000)
85
+ require "openssl"
86
+ OpenSSL::PKCS5.pbkdf2_hmac(
87
+ password,
88
+ salt,
89
+ iterations,
90
+ 32, # 256 bits
91
+ OpenSSL::Digest.new("SHA256"),
92
+ )
93
+ end
94
+ end
95
+ end
96
+ end