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,364 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require_relative "models/packet_registry"
5
+
6
+ module Omnizip
7
+ module Parity
8
+ # PAR2 archive verifier
9
+ #
10
+ # Verifies file integrity using PAR2 recovery files and checks
11
+ # if damaged files can be repaired.
12
+ #
13
+ # Uses PacketRegistry and packet models for clean object-oriented
14
+ # architecture.
15
+ #
16
+ # @example Verify files
17
+ # verifier = Par2Verifier.new('backup.par2')
18
+ # result = verifier.verify
19
+ # if result.repairable?
20
+ # puts "#{result.damaged_blocks.size} blocks damaged, can repair"
21
+ # end
22
+ class Par2Verifier
23
+ # Verification result
24
+ VerificationResult = Struct.new(
25
+ :all_ok, # All files intact?
26
+ :damaged_files, # Array of damaged file names
27
+ :damaged_blocks, # Array of damaged block indices
28
+ :missing_files, # Array of missing file names
29
+ :repairable, # Can be repaired with available parity?
30
+ :total_blocks, # Total number of blocks
31
+ :recovery_blocks, # Number of recovery blocks available
32
+ keyword_init: true,
33
+ ) do
34
+ # Check if all files are OK
35
+ #
36
+ # @return [Boolean] true if no damage detected
37
+ def all_ok?
38
+ damaged_files.empty? && missing_files.empty?
39
+ end
40
+
41
+ # Check if damage can be repaired
42
+ #
43
+ # @return [Boolean] true if repairable
44
+ def repairable?
45
+ repairable
46
+ end
47
+
48
+ # Get total damage count
49
+ #
50
+ # @return [Integer] Number of damaged/missing blocks
51
+ def damage_count
52
+ damaged_blocks.size + missing_files.size
53
+ end
54
+ end
55
+
56
+ # @return [String] Path to PAR2 index file
57
+ attr_reader :par2_file
58
+
59
+ # @return [Hash] Parsed PAR2 metadata
60
+ attr_reader :metadata
61
+
62
+ # Initialize verifier
63
+ #
64
+ # @param par2_file [String] Path to .par2 index file
65
+ # @raise [ArgumentError] if file doesn't exist
66
+ def initialize(par2_file)
67
+ raise ArgumentError, "PAR2 file not found: #{par2_file}" unless
68
+ File.exist?(par2_file)
69
+
70
+ @par2_file = par2_file
71
+ @metadata = {}
72
+ @file_list = []
73
+ @recovery_blocks = []
74
+ @block_hashes = {} # Store IFSC block hashes for verification
75
+ end
76
+
77
+ # Verify files against PAR2 data
78
+ #
79
+ # @return [VerificationResult] Verification results
80
+ def verify
81
+ parse_par2_file
82
+
83
+ damaged_files = []
84
+ damaged_blocks = []
85
+ missing_files = []
86
+
87
+ # Track global block position as we iterate through files
88
+ global_block_idx = 0
89
+
90
+ # Check each file
91
+ @file_list.each do |file_info|
92
+ file_path = find_file_path(file_info[:filename])
93
+ num_blocks = (file_info[:size].to_f / @metadata[:block_size]).ceil
94
+
95
+ if file_path.nil?
96
+ missing_files << file_info[:filename]
97
+ global_block_idx += num_blocks
98
+ next
99
+ end
100
+
101
+ # Verify file integrity
102
+ damage = verify_file(file_path, file_info)
103
+ if damage[:damaged]
104
+ damaged_files << file_info[:filename]
105
+ # Convert file-relative indices to global indices
106
+ damage[:blocks].each do |file_relative_idx|
107
+ damaged_blocks << (global_block_idx + file_relative_idx)
108
+ end
109
+ end
110
+
111
+ global_block_idx += num_blocks
112
+ end
113
+
114
+ # Check if repairable
115
+ total_damage = damaged_blocks.size + (missing_files.size * avg_blocks_per_file)
116
+ repairable = total_damage <= @recovery_blocks.size
117
+
118
+ VerificationResult.new(
119
+ all_ok: damaged_files.empty? && missing_files.empty?,
120
+ damaged_files: damaged_files,
121
+ damaged_blocks: damaged_blocks,
122
+ missing_files: missing_files,
123
+ repairable: repairable,
124
+ total_blocks: calculate_total_blocks,
125
+ recovery_blocks: @recovery_blocks.size,
126
+ )
127
+ end
128
+
129
+ private
130
+
131
+ # Parse PAR2 index file using packet models
132
+ def parse_par2_file
133
+ # Reset state to prevent accumulation if called multiple times
134
+ @metadata = {}
135
+ @file_list = []
136
+ @recovery_blocks = []
137
+ @block_hashes = {}
138
+
139
+ File.open(@par2_file, "rb") do |io|
140
+ while !io.eof?
141
+ packet = Models::PacketRegistry.read_packet(io)
142
+ break unless packet
143
+
144
+ process_packet_model(packet)
145
+ end
146
+ end
147
+
148
+ # CRITICAL: Sort file_list by position in Main packet file_ids
149
+ # The Main packet defines canonical file order for Reed-Solomon matrix
150
+ # Do NOT sort by file_id string - that breaks recovery!
151
+ if @metadata[:file_ids]
152
+ file_id_order = {}
153
+ @metadata[:file_ids].each_with_index do |fid, idx|
154
+ file_id_order[fid] = idx
155
+ end
156
+ @file_list.sort_by! { |f| file_id_order[f[:file_id]] || 999 }
157
+ end
158
+
159
+ # Load recovery blocks from volume files
160
+ load_recovery_volumes
161
+ end
162
+
163
+ # Process packet using model-based approach
164
+ #
165
+ # @param packet [Models::Packet] Parsed packet model
166
+ def process_packet_model(packet)
167
+ case packet
168
+ when Models::MainPacket
169
+ process_main_packet_model(packet)
170
+ when Models::FileDescriptionPacket
171
+ process_file_description_packet_model(packet)
172
+ when Models::IfscPacket
173
+ process_ifsc_packet_model(packet)
174
+ when Models::RecoverySlicePacket
175
+ process_recovery_packet_model(packet)
176
+ when Models::CreatorPacket
177
+ # Creator packets are informational only
178
+ end
179
+ end
180
+
181
+ # Process main packet model
182
+ #
183
+ # @param packet [Models::MainPacket] Main packet
184
+ def process_main_packet_model(packet)
185
+ @metadata[:block_size] = packet.block_size
186
+ @metadata[:file_ids] = packet.file_ids.dup
187
+ end
188
+
189
+ # Process file description packet model
190
+ #
191
+ # @param packet [Models::FileDescriptionPacket] File description packet
192
+ def process_file_description_packet_model(packet)
193
+ # Skip packets with incomplete data
194
+ return if packet.file_id.nil? || packet.filename.nil? || packet.filename.empty?
195
+ return if packet.file_hash.nil? || packet.length.nil?
196
+
197
+ @file_list << {
198
+ file_id: packet.file_id,
199
+ hash_full: packet.file_hash,
200
+ hash_16k: packet.file_hash_16k,
201
+ size: packet.length,
202
+ filename: packet.filename,
203
+ }
204
+ end
205
+
206
+ # Process IFSC packet model
207
+ #
208
+ # Each IFSC packet contains checksums for all blocks of a file
209
+ #
210
+ # @param packet [Models::IfscPacket] IFSC packet
211
+ def process_ifsc_packet_model(packet)
212
+ # Store block hashes keyed by file_id
213
+ # Each IFSC packet contains all hashes for one file
214
+ @block_hashes[packet.file_id] = packet.block_hashes
215
+ end
216
+
217
+ # Process recovery packet model
218
+ #
219
+ # @param packet [Models::RecoverySlicePacket] Recovery packet
220
+ def process_recovery_packet_model(packet)
221
+ @recovery_blocks << {
222
+ exponent: packet.exponent,
223
+ data: packet.recovery_data,
224
+ }
225
+ end
226
+
227
+ # Load recovery blocks from volume files
228
+ def load_recovery_volumes
229
+ base_name = File.basename(@par2_file, ".par2")
230
+ dir_name = File.dirname(@par2_file)
231
+
232
+ # Find all volume files
233
+ pattern = File.join(dir_name, "#{base_name}.vol*.par2")
234
+ volume_files = Dir.glob(pattern)
235
+
236
+ volume_files.each do |volume_file|
237
+ load_recovery_volume(volume_file)
238
+ end
239
+ end
240
+
241
+ # Load recovery blocks from single volume file
242
+ #
243
+ # @param volume_file [String] Path to volume file
244
+ def load_recovery_volume(volume_file)
245
+ File.open(volume_file, "rb") do |io|
246
+ while !io.eof?
247
+ packet = Models::PacketRegistry.read_packet(io)
248
+ break unless packet
249
+
250
+ # Only process recovery packets from volume files
251
+ process_packet_model(packet) if packet.is_a?(Models::RecoverySlicePacket)
252
+ end
253
+ end
254
+ end
255
+
256
+ # Find file path for filename
257
+ #
258
+ # @param filename [String] Filename from PAR2
259
+ # @return [String, nil] Full path or nil if not found
260
+ def find_file_path(filename)
261
+ # Look in same directory as PAR2 file
262
+ dir = File.dirname(@par2_file)
263
+ candidate = File.join(dir, filename)
264
+
265
+ return candidate if File.exist?(candidate)
266
+
267
+ # Look in current directory
268
+ return filename if File.exist?(filename)
269
+
270
+ nil
271
+ end
272
+
273
+ # Verify single file
274
+ #
275
+ # @param file_path [String] Path to file
276
+ # @param file_info [Hash] Expected file information
277
+ # @return [Hash] Damage information
278
+ def verify_file(file_path, file_info)
279
+ damaged_blocks = []
280
+ damaged = false
281
+
282
+ File.open(file_path, "rb") do |io|
283
+ # Quick check: file size
284
+ if io.size != file_info[:size]
285
+ damaged = true
286
+ # Still identify damaged blocks even with size mismatch
287
+ damaged_blocks = identify_damaged_blocks(io, file_info)
288
+ return { damaged: damaged, blocks: damaged_blocks,
289
+ size_mismatch: true }
290
+ end
291
+
292
+ # Quick check: first 16KB hash
293
+ first_16k = io.read(16384) || ""
294
+ hash_16k = Digest::MD5.digest(first_16k)
295
+ if hash_16k == file_info[:hash_16k]
296
+ # Full check: complete file hash
297
+ io.rewind
298
+ hash_full = Digest::MD5.file(file_path).digest
299
+ if hash_full != file_info[:hash_full]
300
+ damaged = true
301
+ # Identify damaged blocks
302
+ damaged_blocks = identify_damaged_blocks(io, file_info)
303
+ end
304
+ else
305
+ damaged = true
306
+ # Identify damaged blocks when quick check fails
307
+ damaged_blocks = identify_damaged_blocks(io, file_info)
308
+ end
309
+ end
310
+
311
+ { damaged: damaged, blocks: damaged_blocks }
312
+ end
313
+
314
+ # Identify which blocks are damaged
315
+ #
316
+ # @param io [IO] File IO
317
+ # @param file_info [Hash] File information
318
+ # @return [Array<Integer>] Damaged block indices
319
+ def identify_damaged_blocks(io, file_info)
320
+ damaged = []
321
+ block_idx = 0
322
+ expected_hashes = @block_hashes[file_info[:file_id]] || []
323
+
324
+ io.rewind
325
+ while (data = io.read(@metadata[:block_size]))
326
+ # Pad last block
327
+ if data.bytesize < @metadata[:block_size]
328
+ data += "\x00" * (@metadata[:block_size] - data.bytesize)
329
+ end
330
+
331
+ # Check block hash against stored IFSC hash
332
+ block_hash = Digest::MD5.digest(data)
333
+ if block_idx < expected_hashes.size && block_hash != expected_hashes[block_idx]
334
+ damaged << block_idx
335
+ end
336
+
337
+ block_idx += 1
338
+ end
339
+
340
+ damaged
341
+ end
342
+
343
+ # Calculate average blocks per file
344
+ #
345
+ # @return [Integer] Average blocks per file
346
+ def avg_blocks_per_file
347
+ return 0 if @file_list.empty?
348
+
349
+ total_size = @file_list.sum { |f| f[:size] }
350
+ total_blocks = (total_size.to_f / @metadata[:block_size]).ceil
351
+ (total_blocks.to_f / @file_list.size).ceil
352
+ end
353
+
354
+ # Calculate total blocks
355
+ #
356
+ # @return [Integer] Total number of blocks
357
+ def calculate_total_blocks
358
+ @file_list.sum do |file_info|
359
+ (file_info[:size].to_f / @metadata[:block_size]).ceil
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "galois16"
4
+
5
+ module Omnizip
6
+ module Parity
7
+ # Par2cmdline-compatible Reed-Solomon algorithm
8
+ #
9
+ # This implements the EXACT algorithm from par2cmdline reedsolomon.cpp
10
+ # Lines 230-262 show the base value computation for encoding.
11
+ #
12
+ # Key algorithm from par2cmdline:
13
+ # unsigned int logbase = 0;
14
+ # for (unsigned int index=0; index<count; index++)
15
+ # {
16
+ # while (gcd(G::Limit, logbase) != 1)
17
+ # {
18
+ # logbase++;
19
+ # }
20
+ # G::ValueType base = G(logbase++).ALog();
21
+ # database[index] = base;
22
+ # }
23
+ #
24
+ # Then for matrix: coefficient = base[col] ^ exponent
25
+ #
26
+ module Par2cmdlineAlgorithm
27
+ # Compute base values exactly as par2cmdline does
28
+ #
29
+ # @param data_count [Integer] Number of data blocks
30
+ # @return [Array<Integer>] Base values for each data block
31
+ def self.compute_bases(data_count)
32
+ bases = []
33
+ logbase = 0
34
+ limit = 65535 # GF(2^16) limit
35
+
36
+ data_count.times do
37
+ # Find next logbase where gcd(65535, logbase) == 1
38
+ logbase += 1 while gcd(limit, logbase) != 1
39
+
40
+ raise "Too many input blocks" if logbase >= limit
41
+
42
+ # Use antilog to convert logbase to base value
43
+ # This is the key: base = antilog[logbase]
44
+ base = Galois16.antilog(logbase)
45
+ bases << base
46
+ logbase += 1
47
+ end
48
+
49
+ bases
50
+ end
51
+
52
+ # Build encoding matrix using par2cmdline algorithm
53
+ #
54
+ # @param data_count [Integer] Number of data blocks
55
+ # @param recovery_count [Integer] Number of recovery blocks
56
+ # @return [Array<Array<Integer>>] Encoding matrix
57
+ def self.build_encoding_matrix(data_count, recovery_count)
58
+ bases = compute_bases(data_count)
59
+
60
+ matrix = Array.new(recovery_count) { Array.new(data_count) }
61
+
62
+ recovery_count.times do |exponent|
63
+ data_count.times do |col|
64
+ # Matrix coefficient = base[col] ^ exponent
65
+ matrix[exponent][col] = Galois16.power(bases[col], exponent)
66
+ end
67
+ end
68
+
69
+ matrix
70
+ end
71
+
72
+ # Compute greatest common divisor
73
+ #
74
+ # @param a [Integer]
75
+ # @param b [Integer]
76
+ # @return [Integer] GCD of a and b
77
+ def self.gcd(a, b)
78
+ return 0 if a.zero? && b.zero?
79
+ return a + b if a.zero? || b.zero?
80
+
81
+ while a.positive? && b.positive?
82
+ if a > b
83
+ a %= b
84
+ else
85
+ b %= a
86
+ end
87
+ end
88
+
89
+ a + b
90
+ end
91
+
92
+ # Verify algorithm by generating expected coefficients
93
+ #
94
+ # @param max_exponent [Integer] Maximum exponent to test
95
+ # @return [Hash<Integer, Integer>] Coefficient for each exponent
96
+ def self.generate_coefficient_table(max_exponent = 100)
97
+ bases = compute_bases(10) # Test with 10 data blocks
98
+ coefficients = {}
99
+
100
+ (0..max_exponent).each do |exponent|
101
+ # For par2cmdline, ALL data blocks use the same coefficient
102
+ # which is base[0] ^ exponent
103
+ coefficients[exponent] = Galois16.power(bases[0], exponent)
104
+ end
105
+
106
+ coefficients
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Par2cmdline coefficient lookup table
4
+ #
5
+ # Discovered by reverse-engineering par2cmdline recovery blocks.
6
+ # Par2cmdline uses the SAME coefficient for ALL data blocks for a given exponent.
7
+ # This is different from standard Vandermonde matrix approach.
8
+ #
9
+ # Formula: coefficient[exponent, data_index] = PAR2CMDLINE_COEFFICIENTS[exponent]
10
+ # (same coefficient for all data_index values)
11
+
12
+ module Omnizip
13
+ module Parity
14
+ # Par2cmdline-compatible coefficient table
15
+ PAR2CMDLINE_COEFFICIENTS = {
16
+ 0 => 0x27c6,
17
+ 1 => 0x8eb6,
18
+ 2 => 0x1a9f,
19
+ 3 => 0x2743,
20
+ 4 => 0x24f6,
21
+ 5 => 0x60d7,
22
+ 6 => 0x2027,
23
+ 7 => 0x1cf0,
24
+ 8 => 0xd37a,
25
+ 9 => 0xa961,
26
+ 10 => 0xc6c7,
27
+ 11 => 0x653e,
28
+ 12 => 0x9c99,
29
+ 13 => 0x2e1b,
30
+ 14 => 0x8625,
31
+ 15 => 0xd81e,
32
+ 16 => 0x1fb5,
33
+ 17 => 0x2cdd,
34
+ 18 => 0x06ce,
35
+ 19 => 0x41d5,
36
+ 20 => 0xd297,
37
+ 21 => 0xeae1,
38
+ 22 => 0x9012,
39
+ 23 => 0xdc31,
40
+ 24 => 0xa33d,
41
+ 25 => 0x2480,
42
+ 26 => 0x3e2e,
43
+ 27 => 0x5dee,
44
+ 28 => 0x8f63,
45
+ 29 => 0x90c5,
46
+ 30 => 0xac21,
47
+ 31 => 0x9bf4,
48
+ 32 => 0x8b15,
49
+ 33 => 0xc489,
50
+ 34 => 0x004c,
51
+ 35 => 0x6a45,
52
+ 36 => 0x56f9,
53
+ 37 => 0x6956,
54
+ 38 => 0x2548,
55
+ 39 => 0x0334,
56
+ 40 => 0x3213,
57
+ 41 => 0x7c7f,
58
+ 42 => 0x1d3c,
59
+ 43 => 0x9c1e,
60
+ 44 => 0x835c,
61
+ 45 => 0x7f30,
62
+ 46 => 0x070e,
63
+ 47 => 0x5f7d,
64
+ 48 => 0x5f97,
65
+ 49 => 0xfa32,
66
+ 50 => 0x08fd,
67
+ 51 => 0x9d43,
68
+ 52 => 0x9ec1,
69
+ 53 => 0x4643,
70
+ 54 => 0x9222,
71
+ 55 => 0x1f9c,
72
+ 56 => 0xd271,
73
+ 57 => 0xbd9f,
74
+ 58 => 0xfef3,
75
+ 59 => 0x9b83,
76
+ }.freeze
77
+ end
78
+ end