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,375 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require_relative "constants"
5
+ require_relative "local_file_header"
6
+ require_relative "central_directory_header"
7
+ require_relative "end_of_central_directory"
8
+ require_relative "unix_extra_field"
9
+ require_relative "../../link_handler"
10
+
11
+ module Omnizip
12
+ module Formats
13
+ module Zip
14
+ # ZIP archive writer
15
+ class Writer
16
+ include Constants
17
+
18
+ attr_reader :file_path, :entries
19
+
20
+ def initialize(file_path)
21
+ @file_path = file_path
22
+ @entries = []
23
+ @local_headers = []
24
+ end
25
+
26
+ # Add a file to the archive
27
+ def add_file(file_path, archive_path = nil, preserve_links: true)
28
+ archive_path ||= File.basename(file_path)
29
+
30
+ if File.directory?(file_path)
31
+ add_directory(archive_path)
32
+ elsif preserve_links && LinkHandler.symlink?(file_path)
33
+ add_symlink(archive_path, LinkHandler.read_link_target(file_path))
34
+ elsif preserve_links && LinkHandler.hardlink?(file_path)
35
+ # For hard links, we add as regular file but track inode
36
+ data = File.binread(file_path)
37
+ add_data(archive_path, data, File.stat(file_path))
38
+ else
39
+ data = File.binread(file_path)
40
+ add_data(archive_path, data, File.stat(file_path))
41
+ end
42
+ end
43
+
44
+ # Add a symbolic link to the archive
45
+ def add_symlink(archive_path, target)
46
+ unless LinkHandler.symlink_supported?
47
+ warn "Warning: Symbolic links not supported on #{RUBY_PLATFORM}, storing as regular file"
48
+ add_data(archive_path, target)
49
+ return
50
+ end
51
+
52
+ entry = create_entry(
53
+ filename: archive_path,
54
+ uncompressed_data: target,
55
+ symlink: true,
56
+ symlink_target: target,
57
+ )
58
+
59
+ @entries << entry
60
+ end
61
+
62
+ # Add a hard link to the archive
63
+ def add_hardlink(archive_path, target_path)
64
+ unless LinkHandler.hardlink_supported?
65
+ warn "Warning: Hard links not supported on #{RUBY_PLATFORM}, storing as regular file"
66
+ # Store the target file content instead
67
+ if File.exist?(target_path)
68
+ data = File.binread(target_path)
69
+ add_data(archive_path, data, File.stat(target_path))
70
+ else
71
+ add_data(archive_path, "")
72
+ end
73
+ return
74
+ end
75
+
76
+ # For hard links, we store the first occurrence as a regular file
77
+ # and subsequent ones reference the original
78
+ entry = create_entry(
79
+ filename: archive_path,
80
+ uncompressed_data: "",
81
+ hardlink: true,
82
+ hardlink_target: target_path,
83
+ )
84
+
85
+ @entries << entry
86
+ end
87
+
88
+ # Add a directory to the archive
89
+ def add_directory(archive_path)
90
+ archive_path = "#{archive_path}/" unless archive_path.end_with?("/")
91
+
92
+ entry = create_entry(
93
+ filename: archive_path,
94
+ uncompressed_data: "",
95
+ directory: true,
96
+ )
97
+
98
+ @entries << entry
99
+ end
100
+
101
+ # Add data directly to the archive
102
+ def add_data(archive_path, data, stat = nil)
103
+ entry = create_entry(
104
+ filename: archive_path,
105
+ uncompressed_data: data,
106
+ stat: stat,
107
+ )
108
+
109
+ @entries << entry
110
+ end
111
+
112
+ # Write the archive to disk
113
+ def write(compression_method: COMPRESSION_DEFLATE, level: 6)
114
+ File.open(file_path, "wb") do |io|
115
+ write_to_io(io, compression_method: compression_method,
116
+ level: level)
117
+ end
118
+ end
119
+
120
+ # Write to an IO object
121
+ def write_to_io(io, compression_method: COMPRESSION_DEFLATE, level: 6)
122
+ local_header_offsets = []
123
+
124
+ # Write local file headers and data
125
+ entries.each do |entry|
126
+ offset = io.pos
127
+ local_header_offsets << offset
128
+
129
+ # Create local file header
130
+ local_header = create_local_header(entry, compression_method)
131
+
132
+ # Compress data if not a directory
133
+ if entry[:directory]
134
+ compressed_data = ""
135
+ entry[:compressed_size] = 0
136
+ entry[:uncompressed_size] = 0
137
+ entry[:crc32] = 0
138
+ else
139
+ compressed_data = compress_data(
140
+ entry[:uncompressed_data],
141
+ compression_method,
142
+ level,
143
+ )
144
+ entry[:compressed_size] = compressed_data.bytesize
145
+ entry[:uncompressed_size] = entry[:uncompressed_data].bytesize
146
+ entry[:crc32] = calculate_crc32(entry[:uncompressed_data])
147
+ end
148
+
149
+ # Update local header with compressed sizes
150
+ local_header.compressed_size = entry[:compressed_size]
151
+ local_header.uncompressed_size = entry[:uncompressed_size]
152
+ local_header.crc32 = entry[:crc32]
153
+
154
+ # Write local header
155
+ io.write(local_header.to_binary)
156
+
157
+ # Write compressed data
158
+ io.write(compressed_data) unless entry[:directory]
159
+ end
160
+
161
+ # Record start of central directory
162
+ central_directory_offset = io.pos
163
+
164
+ # Write central directory headers
165
+ entries.each_with_index do |entry, index|
166
+ central_header = create_central_header(
167
+ entry,
168
+ compression_method,
169
+ local_header_offsets[index],
170
+ )
171
+ io.write(central_header.to_binary)
172
+ end
173
+
174
+ # Calculate central directory size
175
+ central_directory_size = io.pos - central_directory_offset
176
+
177
+ # Write end of central directory record
178
+ eocd = create_eocd(
179
+ total_entries: entries.size,
180
+ central_directory_size: central_directory_size,
181
+ central_directory_offset: central_directory_offset,
182
+ )
183
+ io.write(eocd.to_binary)
184
+ end
185
+
186
+ private
187
+
188
+ # Create an entry hash
189
+ def create_entry(
190
+ filename:,
191
+ uncompressed_data:,
192
+ directory: false,
193
+ stat: nil,
194
+ symlink: false,
195
+ symlink_target: nil,
196
+ hardlink: false,
197
+ hardlink_target: nil
198
+ )
199
+ now = Time.now
200
+
201
+ {
202
+ filename: filename,
203
+ uncompressed_data: uncompressed_data,
204
+ directory: directory,
205
+ stat: stat,
206
+ mtime: now,
207
+ compressed_size: 0,
208
+ uncompressed_size: uncompressed_data.bytesize,
209
+ crc32: 0,
210
+ symlink: symlink,
211
+ symlink_target: symlink_target,
212
+ hardlink: hardlink,
213
+ hardlink_target: hardlink_target,
214
+ }
215
+ end
216
+
217
+ # Create local file header from entry
218
+ def create_local_header(entry, compression_method)
219
+ mtime = entry[:mtime]
220
+
221
+ LocalFileHeader.new(
222
+ version_needed: version_for_method(compression_method),
223
+ flags: FLAG_UTF8,
224
+ compression_method: entry[:directory] ? COMPRESSION_STORE : compression_method,
225
+ last_mod_time: dos_time(mtime),
226
+ last_mod_date: dos_date(mtime),
227
+ crc32: 0, # Will be updated after compression
228
+ compressed_size: 0, # Will be updated after compression
229
+ uncompressed_size: 0, # Will be updated after compression
230
+ filename: entry[:filename],
231
+ )
232
+ end
233
+
234
+ # Create central directory header from entry
235
+ def create_central_header(entry, compression_method,
236
+ local_header_offset)
237
+ mtime = entry[:mtime]
238
+
239
+ # Build extra field for links
240
+ extra_field = ""
241
+ if entry[:symlink] && entry[:symlink_target]
242
+ unix_field = UnixExtraField.for_symlink(entry[:symlink_target])
243
+ extra_field = unix_field.to_binary
244
+ elsif entry[:hardlink] && entry[:hardlink_target]
245
+ unix_field = UnixExtraField.for_hardlink
246
+ extra_field = unix_field.to_binary
247
+ end
248
+
249
+ external_attrs = if entry[:directory]
250
+ UNIX_DIR_PERMISSIONS | ATTR_DIRECTORY
251
+ elsif entry[:symlink]
252
+ UNIX_SYMLINK_PERMISSIONS
253
+ elsif entry[:stat]
254
+ (entry[:stat].mode & 0o777) << 16
255
+ else
256
+ UNIX_FILE_PERMISSIONS
257
+ end
258
+
259
+ CentralDirectoryHeader.new(
260
+ version_made_by: VERSION_MADE_BY_UNIX | version_for_method(compression_method),
261
+ version_needed: version_for_method(compression_method),
262
+ flags: FLAG_UTF8,
263
+ compression_method: entry[:directory] ? COMPRESSION_STORE : compression_method,
264
+ last_mod_time: dos_time(mtime),
265
+ last_mod_date: dos_date(mtime),
266
+ crc32: entry[:crc32],
267
+ compressed_size: entry[:compressed_size],
268
+ uncompressed_size: entry[:uncompressed_size],
269
+ disk_number_start: 0,
270
+ internal_attributes: 0,
271
+ external_attributes: external_attrs,
272
+ local_header_offset: local_header_offset,
273
+ filename: entry[:filename],
274
+ extra_field: extra_field,
275
+ )
276
+ end
277
+
278
+ # Create end of central directory record
279
+ def create_eocd(total_entries:, central_directory_size:,
280
+ central_directory_offset:)
281
+ EndOfCentralDirectory.new(
282
+ disk_number: 0,
283
+ disk_number_with_cd: 0,
284
+ total_entries_this_disk: total_entries,
285
+ total_entries: total_entries,
286
+ central_directory_size: central_directory_size,
287
+ central_directory_offset: central_directory_offset,
288
+ )
289
+ end
290
+
291
+ # Compress data based on compression method
292
+ def compress_data(data, method, level)
293
+ case method
294
+ when COMPRESSION_STORE
295
+ data
296
+ when COMPRESSION_DEFLATE
297
+ compress_deflate(data, level)
298
+ when COMPRESSION_BZIP2
299
+ compress_bzip2(data, level)
300
+ when COMPRESSION_LZMA
301
+ compress_lzma(data, level)
302
+ when COMPRESSION_ZSTANDARD
303
+ compress_zstandard(data, level)
304
+ else
305
+ raise Omnizip::UnsupportedFormatError,
306
+ "Unsupported compression method: #{method}"
307
+ end
308
+ end
309
+
310
+ # Compress using Deflate
311
+ def compress_deflate(data, level)
312
+ require "zlib"
313
+ # ZIP uses raw deflate without zlib wrapper
314
+ Zlib::Deflate.new(level, -Zlib::MAX_WBITS).deflate(data, Zlib::FINISH)
315
+ rescue StandardError => e
316
+ raise Omnizip::CompressionError,
317
+ "Deflate compression failed: #{e.message}"
318
+ end
319
+
320
+ # Compress using BZip2
321
+ def compress_bzip2(data, level)
322
+ algorithm = AlgorithmRegistry.get(:bzip2)
323
+ algorithm.compress(data, level: level)
324
+ rescue StandardError => e
325
+ raise Omnizip::CompressionError,
326
+ "BZip2 compression failed: #{e.message}"
327
+ end
328
+
329
+ # Compress using LZMA
330
+ def compress_lzma(data, level)
331
+ algorithm = AlgorithmRegistry.get(:lzma)
332
+ algorithm.compress(data, level: level)
333
+ rescue StandardError => e
334
+ raise Omnizip::CompressionError,
335
+ "LZMA compression failed: #{e.message}"
336
+ end
337
+
338
+ # Compress using Zstandard
339
+ def compress_zstandard(data, level)
340
+ algorithm = AlgorithmRegistry.get(:zstandard)
341
+ algorithm.compress(data, level: level)
342
+ rescue StandardError => e
343
+ raise Omnizip::CompressionError,
344
+ "Zstandard compression failed: #{e.message}"
345
+ end
346
+
347
+ # Calculate CRC32 checksum
348
+ def calculate_crc32(data)
349
+ Omnizip::Checksums::Crc32.new.tap { |c| c.update(data) }.finalize
350
+ end
351
+
352
+ # Get version needed for compression method
353
+ def version_for_method(method)
354
+ case method
355
+ when COMPRESSION_STORE then VERSION_DEFAULT
356
+ when COMPRESSION_DEFLATE then VERSION_DEFLATE
357
+ when COMPRESSION_BZIP2 then VERSION_BZIP2
358
+ when COMPRESSION_LZMA then VERSION_LZMA
359
+ else VERSION_DEFAULT
360
+ end
361
+ end
362
+
363
+ # Convert Time to DOS time format
364
+ def dos_time(time)
365
+ ((time.hour << 11) | (time.min << 5) | (time.sec / 2)) & 0xFFFF
366
+ end
367
+
368
+ # Convert Time to DOS date format
369
+ def dos_date(time)
370
+ (((time.year - 1980) << 9) | (time.month << 5) | time.day) & 0xFFFF
371
+ end
372
+ end
373
+ end
374
+ end
375
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+
5
+ module Omnizip
6
+ module Formats
7
+ module Zip
8
+ # ZIP64 End of Central Directory Record
9
+ # Used when the archive exceeds ZIP format limits (>4GB or >65535 entries)
10
+ class Zip64EndOfCentralDirectory
11
+ include Constants
12
+
13
+ attr_accessor :signature, :record_size, :version_made_by, :version_needed,
14
+ :disk_number, :disk_number_with_cd,
15
+ :total_entries_this_disk, :total_entries,
16
+ :central_directory_size, :central_directory_offset,
17
+ :extensible_data_sector
18
+
19
+ def initialize(
20
+ signature: ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE,
21
+ record_size: 44, # Size of remaining record (not including signature and size field)
22
+ version_made_by: VERSION_MADE_BY_UNIX | VERSION_ZIP64,
23
+ version_needed: VERSION_ZIP64,
24
+ disk_number: 0,
25
+ disk_number_with_cd: 0,
26
+ total_entries_this_disk: 0,
27
+ total_entries: 0,
28
+ central_directory_size: 0,
29
+ central_directory_offset: 0,
30
+ extensible_data_sector: ""
31
+ )
32
+ @signature = signature
33
+ @record_size = record_size
34
+ @version_made_by = version_made_by
35
+ @version_needed = version_needed
36
+ @disk_number = disk_number
37
+ @disk_number_with_cd = disk_number_with_cd
38
+ @total_entries_this_disk = total_entries_this_disk
39
+ @total_entries = total_entries
40
+ @central_directory_size = central_directory_size
41
+ @central_directory_offset = central_directory_offset
42
+ @extensible_data_sector = extensible_data_sector
43
+ end
44
+
45
+ # Serialize to binary format
46
+ def to_binary
47
+ @record_size = 44 + extensible_data_sector.bytesize
48
+
49
+ [
50
+ signature,
51
+ record_size,
52
+ version_made_by,
53
+ version_needed,
54
+ disk_number,
55
+ disk_number_with_cd,
56
+ total_entries_this_disk,
57
+ total_entries,
58
+ central_directory_size,
59
+ central_directory_offset,
60
+ ].pack("VQvvVVQQQQ") + extensible_data_sector.b
61
+ end
62
+
63
+ # Parse from binary data
64
+ def self.from_binary(data)
65
+ signature, record_size, version_made_by, version_needed,
66
+ disk_number, disk_number_with_cd,
67
+ total_entries_this_disk, total_entries,
68
+ central_directory_size, central_directory_offset = data.unpack("VQvvVVQQQQ")
69
+
70
+ unless signature == ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE
71
+ raise Omnizip::FormatError,
72
+ "Invalid ZIP64 EOCD signature"
73
+ end
74
+
75
+ # Extensible data sector starts after the fixed 56 bytes (4+8+2+2+4+4+8+8+8+8)
76
+ extensible_data_sector = if data.bytesize > 56
77
+ data[56..]
78
+ else
79
+ ""
80
+ end
81
+
82
+ new(
83
+ signature: signature,
84
+ record_size: record_size,
85
+ version_made_by: version_made_by,
86
+ version_needed: version_needed,
87
+ disk_number: disk_number,
88
+ disk_number_with_cd: disk_number_with_cd,
89
+ total_entries_this_disk: total_entries_this_disk,
90
+ total_entries: total_entries,
91
+ central_directory_size: central_directory_size,
92
+ central_directory_offset: central_directory_offset,
93
+ extensible_data_sector: extensible_data_sector,
94
+ )
95
+ end
96
+
97
+ # Total size of this record in bytes
98
+ def total_size
99
+ 12 + record_size # 4 (signature) + 8 (record_size) + record_size
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+
5
+ module Omnizip
6
+ module Formats
7
+ module Zip
8
+ # ZIP64 End of Central Directory Locator
9
+ # Points to the ZIP64 End of Central Directory Record
10
+ class Zip64EndOfCentralDirectoryLocator
11
+ include Constants
12
+
13
+ attr_accessor :signature, :disk_number_with_zip64_eocd,
14
+ :zip64_eocd_offset, :total_disks
15
+
16
+ def initialize(
17
+ signature: ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE,
18
+ disk_number_with_zip64_eocd: 0,
19
+ zip64_eocd_offset: 0,
20
+ total_disks: 1
21
+ )
22
+ @signature = signature
23
+ @disk_number_with_zip64_eocd = disk_number_with_zip64_eocd
24
+ @zip64_eocd_offset = zip64_eocd_offset
25
+ @total_disks = total_disks
26
+ end
27
+
28
+ # Serialize to binary format
29
+ def to_binary
30
+ [
31
+ signature,
32
+ disk_number_with_zip64_eocd,
33
+ zip64_eocd_offset,
34
+ total_disks,
35
+ ].pack("VVQV")
36
+ end
37
+
38
+ # Parse from binary data
39
+ def self.from_binary(data)
40
+ signature, disk_number, offset, total_disks = data.unpack("VVQV")
41
+
42
+ unless signature == ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE
43
+ raise Omnizip::FormatError,
44
+ "Invalid ZIP64 EOCD Locator signature"
45
+ end
46
+
47
+ new(
48
+ signature: signature,
49
+ disk_number_with_zip64_eocd: disk_number,
50
+ zip64_eocd_offset: offset,
51
+ total_disks: total_disks,
52
+ )
53
+ end
54
+
55
+ # Size of this record in bytes (always 20 bytes)
56
+ def self.record_size
57
+ 20
58
+ end
59
+
60
+ def record_size
61
+ self.class.record_size
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+
5
+ module Omnizip
6
+ module Formats
7
+ module Zip
8
+ # ZIP64 Extended Information Extra Field
9
+ # Used in local/central headers when sizes exceed 32-bit limits
10
+ class Zip64ExtraField
11
+ include Constants
12
+
13
+ attr_accessor :tag, :size, :uncompressed_size, :compressed_size,
14
+ :relative_header_offset, :disk_start_number
15
+
16
+ def initialize(
17
+ tag: ZIP64_EXTRA_FIELD_TAG,
18
+ uncompressed_size: nil,
19
+ compressed_size: nil,
20
+ relative_header_offset: nil,
21
+ disk_start_number: nil
22
+ )
23
+ @tag = tag
24
+ @uncompressed_size = uncompressed_size
25
+ @compressed_size = compressed_size
26
+ @relative_header_offset = relative_header_offset
27
+ @disk_start_number = disk_start_number
28
+ @size = calculate_size
29
+ end
30
+
31
+ # Serialize to binary format
32
+ def to_binary
33
+ @size = calculate_size
34
+ data = [tag, size].pack("vv")
35
+
36
+ # Fields are included in the order they're needed
37
+ # based on which values in the regular header are 0xFFFFFFFF
38
+ data << [uncompressed_size].pack("Q") if uncompressed_size
39
+ data << [compressed_size].pack("Q") if compressed_size
40
+ data << [relative_header_offset].pack("Q") if relative_header_offset
41
+ data << [disk_start_number].pack("V") if disk_start_number
42
+
43
+ data
44
+ end
45
+
46
+ # Parse from binary data
47
+ # needs_uncompressed, needs_compressed, needs_offset, needs_disk specify
48
+ # which fields should be present based on the regular header values
49
+ def self.from_binary(data, needs_uncompressed: false, needs_compressed: false,
50
+ needs_offset: false, needs_disk: false)
51
+ tag, = data.unpack("vv")
52
+
53
+ unless tag == ZIP64_EXTRA_FIELD_TAG
54
+ raise Omnizip::FormatError,
55
+ "Invalid ZIP64 extra field tag"
56
+ end
57
+
58
+ offset = 4 # After tag and size
59
+ uncompressed_size = nil
60
+ compressed_size = nil
61
+ relative_header_offset = nil
62
+ disk_start_number = nil
63
+
64
+ # Read fields in order based on what's needed
65
+ if needs_uncompressed && offset + 8 <= data.bytesize
66
+ uncompressed_size = data[offset, 8].unpack1("Q")
67
+ offset += 8
68
+ end
69
+
70
+ if needs_compressed && offset + 8 <= data.bytesize
71
+ compressed_size = data[offset, 8].unpack1("Q")
72
+ offset += 8
73
+ end
74
+
75
+ if needs_offset && offset + 8 <= data.bytesize
76
+ relative_header_offset = data[offset, 8].unpack1("Q")
77
+ offset += 8
78
+ end
79
+
80
+ if needs_disk && offset + 4 <= data.bytesize
81
+ disk_start_number = data[offset, 4].unpack1("V")
82
+ end
83
+
84
+ new(
85
+ tag: tag,
86
+ uncompressed_size: uncompressed_size,
87
+ compressed_size: compressed_size,
88
+ relative_header_offset: relative_header_offset,
89
+ disk_start_number: disk_start_number,
90
+ )
91
+ end
92
+
93
+ # Check if this extra field is needed
94
+ def self.needed?(uncompressed_size: 0, compressed_size: 0, offset: 0)
95
+ uncompressed_size >= ZIP64_LIMIT ||
96
+ compressed_size >= ZIP64_LIMIT ||
97
+ offset >= ZIP64_LIMIT
98
+ end
99
+
100
+ private
101
+
102
+ # Calculate the size field value
103
+ def calculate_size
104
+ size = 0
105
+ size += 8 if uncompressed_size
106
+ size += 8 if compressed_size
107
+ size += 8 if relative_header_offset
108
+ size += 4 if disk_start_number
109
+ size
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "zip/constants"
4
+ require_relative "zip/local_file_header"
5
+ require_relative "zip/central_directory_header"
6
+ require_relative "zip/end_of_central_directory"
7
+ require_relative "zip/reader"
8
+ require_relative "zip/writer"
9
+
10
+ module Omnizip
11
+ module Formats
12
+ # ZIP archive format implementation
13
+ module Zip
14
+ class << self
15
+ # Read a ZIP archive
16
+ def read(file_path)
17
+ Reader.new(file_path).read
18
+ end
19
+
20
+ # Create a ZIP archive
21
+ def create(file_path, &block)
22
+ writer = Writer.new(file_path)
23
+ yield(writer) if block
24
+ writer
25
+ end
26
+
27
+ # Extract a ZIP archive
28
+ def extract(file_path, output_dir)
29
+ reader = read(file_path)
30
+ reader.extract_all(output_dir)
31
+ end
32
+
33
+ # List entries in a ZIP archive
34
+ def list(file_path)
35
+ reader = read(file_path)
36
+ reader.list_entries
37
+ end
38
+
39
+ # Auto-register .zip format when loaded
40
+ def register!
41
+ require_relative "../format_registry"
42
+ FormatRegistry.register(".zip", Omnizip::Formats::Zip)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ # Auto-register on load
50
+ Omnizip::Formats::Zip.register!