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,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+ require_relative "header"
5
+ require_relative "entry"
6
+
7
+ module Omnizip
8
+ module Formats
9
+ module Tar
10
+ # TAR archive writer
11
+ #
12
+ # Creates TAR archives
13
+ class Writer
14
+ include Constants
15
+
16
+ attr_reader :file_path
17
+
18
+ # Initialize TAR writer
19
+ #
20
+ # @param file_path [String] Path to output TAR archive
21
+ def initialize(file_path)
22
+ @file_path = file_path
23
+ @file = nil
24
+ @closed = false
25
+ end
26
+
27
+ # Add a file to the TAR archive
28
+ #
29
+ # @param entry_name [String] Name/path in archive
30
+ # @param source_path [String] Source file path
31
+ # @param options [Hash] Entry options
32
+ def add(entry_name, source_path = nil, options = {})
33
+ source_path ||= entry_name
34
+
35
+ unless File.exist?(source_path)
36
+ raise ArgumentError, "File not found: #{source_path}"
37
+ end
38
+
39
+ open_file unless @file
40
+
41
+ if File.directory?(source_path)
42
+ add_directory(entry_name, source_path, options)
43
+ elsif File.symlink?(source_path)
44
+ add_symlink(entry_name, source_path, options)
45
+ else
46
+ add_file(entry_name, source_path, options)
47
+ end
48
+ end
49
+
50
+ # Add a file entry
51
+ #
52
+ # @param entry_name [String] Name in archive
53
+ # @param source_path [String] Source file path
54
+ # @param options [Hash] Entry options
55
+ def add_file(entry_name, source_path, options = {})
56
+ open_file unless @file
57
+
58
+ stat = File.stat(source_path)
59
+ data = File.binread(source_path)
60
+
61
+ entry = Entry.new(entry_name, {
62
+ mode: options[:mode] || (stat.mode & 0o777),
63
+ uid: options[:uid] || stat.uid,
64
+ gid: options[:gid] || stat.gid,
65
+ size: data.bytesize,
66
+ mtime: options[:mtime] || stat.mtime,
67
+ typeflag: TYPE_REGULAR,
68
+ uname: options[:uname] || "",
69
+ gname: options[:gname] || "",
70
+ })
71
+
72
+ write_entry(entry, data)
73
+ end
74
+
75
+ # Add a directory entry
76
+ #
77
+ # @param entry_name [String] Directory name in archive
78
+ # @param source_path [String] Source directory path (optional)
79
+ # @param options [Hash] Entry options
80
+ def add_directory(entry_name, source_path = nil, options = {})
81
+ open_file unless @file
82
+
83
+ # Ensure directory name ends with /
84
+ entry_name = "#{entry_name}/" unless entry_name.end_with?("/")
85
+
86
+ if source_path && File.exist?(source_path)
87
+ stat = File.stat(source_path)
88
+ mode = stat.mode & 0o777
89
+ uid = stat.uid
90
+ gid = stat.gid
91
+ mtime = stat.mtime
92
+ else
93
+ mode = 0o755
94
+ uid = 0
95
+ gid = 0
96
+ mtime = Time.now
97
+ end
98
+
99
+ entry = Entry.new(entry_name, {
100
+ mode: options[:mode] || mode,
101
+ uid: options[:uid] || uid,
102
+ gid: options[:gid] || gid,
103
+ size: 0,
104
+ mtime: options[:mtime] || mtime,
105
+ typeflag: TYPE_DIRECTORY,
106
+ uname: options[:uname] || "",
107
+ gname: options[:gname] || "",
108
+ })
109
+
110
+ write_entry(entry, nil)
111
+ end
112
+
113
+ # Add a symbolic link entry
114
+ #
115
+ # @param entry_name [String] Link name in archive
116
+ # @param source_path [String] Source symlink path
117
+ # @param options [Hash] Entry options
118
+ def add_symlink(entry_name, source_path, options = {})
119
+ open_file unless @file
120
+
121
+ linkname = File.readlink(source_path)
122
+ stat = File.lstat(source_path)
123
+
124
+ entry = Entry.new(entry_name, {
125
+ mode: options[:mode] || 0o777,
126
+ uid: options[:uid] || stat.uid,
127
+ gid: options[:gid] || stat.gid,
128
+ size: 0,
129
+ mtime: options[:mtime] || stat.mtime,
130
+ typeflag: TYPE_SYMLINK,
131
+ linkname: linkname,
132
+ uname: options[:uname] || "",
133
+ gname: options[:gname] || "",
134
+ })
135
+
136
+ write_entry(entry, nil)
137
+ end
138
+
139
+ # Add raw entry data
140
+ #
141
+ # @param entry_name [String] Entry name
142
+ # @param data [String] Entry data
143
+ # @param options [Hash] Entry options
144
+ def add_data(entry_name, data, options = {})
145
+ open_file unless @file
146
+
147
+ entry = Entry.new(entry_name, {
148
+ mode: options[:mode] || 0o644,
149
+ uid: options[:uid] || 0,
150
+ gid: options[:gid] || 0,
151
+ size: data.bytesize,
152
+ mtime: options[:mtime] || Time.now,
153
+ typeflag: TYPE_REGULAR,
154
+ uname: options[:uname] || "",
155
+ gname: options[:gname] || "",
156
+ })
157
+
158
+ write_entry(entry, data)
159
+ end
160
+
161
+ # Close the TAR archive
162
+ def close
163
+ return if @closed
164
+
165
+ if @file
166
+ # Write two zero blocks to mark end of archive
167
+ @file.write("\0" * BLOCK_SIZE * 2)
168
+ @file.close
169
+ end
170
+
171
+ @closed = true
172
+ end
173
+
174
+ # Create TAR archive with block syntax
175
+ #
176
+ # @param file_path [String] Path to output TAR archive
177
+ # @yield [Writer] Writer instance
178
+ def self.create(file_path)
179
+ writer = new(file_path)
180
+ yield writer if block_given?
181
+ writer.close
182
+ writer
183
+ end
184
+
185
+ private
186
+
187
+ # Open output file
188
+ def open_file
189
+ @file = File.open(@file_path, "wb")
190
+ end
191
+
192
+ # Write an entry to the archive
193
+ #
194
+ # @param entry [Entry] Entry to write
195
+ # @param data [String, nil] Entry data
196
+ def write_entry(entry, data)
197
+ # Write header
198
+ header = Header.build(entry)
199
+ @file.write(header)
200
+
201
+ # Write data if present
202
+ if data && !data.empty?
203
+ @file.write(data)
204
+
205
+ # Pad to block boundary
206
+ remainder = data.bytesize % BLOCK_SIZE
207
+ if remainder.positive?
208
+ padding = BLOCK_SIZE - remainder
209
+ @file.write("\0" * padding)
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "tar/constants"
4
+ require_relative "tar/entry"
5
+ require_relative "tar/header"
6
+ require_relative "tar/reader"
7
+ require_relative "tar/writer"
8
+
9
+ module Omnizip
10
+ module Formats
11
+ # TAR archive format implementation
12
+ #
13
+ # TAR (Tape Archive) is a simple archive format that bundles files
14
+ # without compression. It's commonly used in combination with
15
+ # compression formats like GZIP (.tar.gz) or BZIP2 (.tar.bz2).
16
+ #
17
+ # This implementation supports POSIX ustar format with:
18
+ # - Regular files
19
+ # - Directories
20
+ # - Symbolic links
21
+ # - Hard links
22
+ # - Metadata preservation (permissions, timestamps, ownership)
23
+ module Tar
24
+ class << self
25
+ # Read a TAR archive
26
+ #
27
+ # @param file_path [String] Path to TAR archive
28
+ # @return [Reader] TAR reader instance
29
+ def read(file_path)
30
+ Reader.new(file_path).read
31
+ end
32
+
33
+ # Open a TAR archive with block syntax
34
+ #
35
+ # @param file_path [String] Path to TAR archive
36
+ # @yield [Reader] TAR reader instance
37
+ # @return [Reader] TAR reader instance
38
+ # rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
39
+ def open(file_path, &block)
40
+ Reader.open(file_path, &block)
41
+ end
42
+ # rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding
43
+
44
+ # Create a TAR archive
45
+ #
46
+ # @param file_path [String] Path to output TAR archive
47
+ # @yield [Writer] TAR writer instance
48
+ # @return [Writer] TAR writer instance
49
+ # rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
50
+ def create(file_path, &block)
51
+ Writer.create(file_path, &block)
52
+ end
53
+ # rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding
54
+
55
+ # Extract a TAR archive
56
+ #
57
+ # @param file_path [String] Path to TAR archive
58
+ # @param output_dir [String] Output directory
59
+ def extract(file_path, output_dir)
60
+ reader = read(file_path)
61
+ reader.extract_all(output_dir)
62
+ end
63
+
64
+ # List entries in a TAR archive
65
+ #
66
+ # @param file_path [String] Path to TAR archive
67
+ # @return [Array<Entry>] List of entries
68
+ def list(file_path)
69
+ reader = read(file_path)
70
+ reader.list_entries
71
+ end
72
+
73
+ # Register TAR format when loaded
74
+ def register!
75
+ require_relative "../format_registry"
76
+ FormatRegistry.register(".tar", Omnizip::Formats::Tar)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ # Auto-register on load
84
+ Omnizip::Formats::Tar.register!
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2025 Ribose Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a
6
+ # copy of this software and associated documentation files (the "Software"),
7
+ # to deal in the Software without restriction, including without limitation
8
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+ # and/or sell copies of the Software, and to permit persons to whom the
10
+ # Software is furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ # DEALINGS IN THE SOFTWARE.
22
+
23
+ require "stringio"
24
+ require_relative "../xz_impl/stream_decoder"
25
+
26
+ module Omnizip
27
+ module Formats
28
+ class Xz
29
+ # XZ format reader
30
+ #
31
+ # Reads and decompresses .xz files compatible with XZ Utils.
32
+ # Provides both low-level stream API and high-level convenience methods.
33
+ class Reader
34
+ # Initialize reader
35
+ #
36
+ # @param input [String, IO] File path or IO object
37
+ def initialize(input)
38
+ @input = if input.is_a?(String)
39
+ File.open(input, "rb")
40
+ elsif input.respond_to?(:read)
41
+ input
42
+ else
43
+ raise ArgumentError,
44
+ "Input must be a file path or IO object"
45
+ end
46
+ @close_on_finish = input.is_a?(String)
47
+ end
48
+
49
+ # Read and decompress XZ data
50
+ #
51
+ # @return [String] Decompressed data
52
+ def read
53
+ XzFormat::StreamDecoder.decode(@input)
54
+ ensure
55
+ close if @close_on_finish
56
+ end
57
+
58
+ # Close the input stream if we opened it
59
+ def close
60
+ @input.close if @input.respond_to?(:close) && !@input.closed?
61
+ end
62
+
63
+ # Check if reader is open
64
+ #
65
+ # @return [Boolean] True if input is open
66
+ def closed?
67
+ @input.respond_to?(:closed?) ? @input.closed? : false
68
+ end
69
+
70
+ # Read in a streaming fashion (for large files)
71
+ #
72
+ # @yield [String] Chunks of decompressed data
73
+ # @return [String] Full decompressed data
74
+ def each_chunk(chunk_size = 64 * 1024)
75
+ # For now, just read everything and yield chunks
76
+ # TODO: Implement true streaming for memory efficiency
77
+ data = read
78
+ offset = 0
79
+ while offset < data.bytesize
80
+ chunk = data.byteslice(offset, chunk_size)
81
+ yield chunk
82
+ offset += chunk.bytesize
83
+ end
84
+ data
85
+ end
86
+ end
87
+
88
+ # Entry class for extract API compatibility
89
+ # XZ format is a single stream, so this is a simple wrapper
90
+ class Entry
91
+ attr_reader :data
92
+
93
+ def initialize(data)
94
+ @data = data
95
+ end
96
+
97
+ # Read the decompressed data
98
+ #
99
+ # @return [String] Decompressed data
100
+ def read
101
+ @data
102
+ end
103
+
104
+ # Get data size
105
+ #
106
+ # @return [Integer] Size in bytes
107
+ def size
108
+ @data.bytesize
109
+ end
110
+
111
+ # Alias for compatibility
112
+ alias_method :bytesize, :size
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "xz_impl/constants"
4
+ require_relative "xz_impl/stream_encoder"
5
+ require_relative "xz_impl/writer"
6
+ require_relative "xz/reader"
7
+ require_relative "../algorithms/lzma"
8
+
9
+ module Omnizip
10
+ module Formats
11
+ # XZ compression format
12
+ # Creates .xz files compatible with XZ Utils
13
+ class Xz
14
+ class << self
15
+ # Create a .xz file from input data
16
+ # @param input [String, IO] Input data to compress
17
+ # @param output [String, IO] Output file path or IO object
18
+ # @param options [Hash] Compression options
19
+ # @option options [Integer] :dict_size Dictionary size (default: 8MB to match XZ Utils preset 6)
20
+ # @option options [Integer] :check Check type (default: CRC64)
21
+ def create(input, output = nil, options = {})
22
+ encoder = XzFormat::StreamEncoder.new(
23
+ check_type: options[:check] || XzConst::CHECK_CRC64,
24
+ dict_size: options[:dict_size] || (64 * 1024 * 1024),
25
+ )
26
+
27
+ compressed = encoder.encode(input)
28
+
29
+ if output
30
+ if output.respond_to?(:write)
31
+ output.write(compressed)
32
+ else
33
+ File.binwrite(output, compressed)
34
+ end
35
+ else
36
+ compressed
37
+ end
38
+ end
39
+
40
+ # Convenience method with block syntax
41
+ # @example
42
+ # Xz.create_file('output.xz') do |xz|
43
+ # xz.add_data('Hello, XZ!')
44
+ # end
45
+ def create_file(path, options = {})
46
+ builder = Builder.new(options)
47
+ yield builder if block_given?
48
+
49
+ compressed = create(builder.data, nil, options)
50
+ File.binwrite(path, compressed)
51
+ end
52
+
53
+ # Decode XZ data (alias for decompress with no output)
54
+ # @param input [String, IO] Input XZ data or file path
55
+ # @return [String] Decompressed data
56
+ def decode(input)
57
+ decompress(input)
58
+ end
59
+
60
+ # Decompress XZ, LZIP (.lz), or LZMA_Alone (.lzma) data
61
+ #
62
+ # This method automatically detects the format based on magic bytes and
63
+ # routes to the appropriate decoder:
64
+ # - XZ format (.xz): Container format with stream header/footer/index
65
+ # - LZIP format (.lz): Standalone format with "LZIP" magic and CRC32 footer
66
+ # - LZMA_Alone format (.lzma): Legacy standalone format with properties byte
67
+ #
68
+ # @param input [String, IO] Input data or file path
69
+ # @param output [String, IO, nil] Output file path or IO object
70
+ # @param options [Hash] Options (reserved for future use)
71
+ # @return [String, nil] Decompressed data (if output is nil)
72
+ def decompress(input, output = nil, _options = {})
73
+ # Handle raw data string vs file path
74
+ data = if input.respond_to?(:read)
75
+ # Already an IO object - read content
76
+ if input.respond_to?(:size)
77
+ # Seekable IO (File, etc.) - read without consuming
78
+ original_pos = input.pos
79
+ content = input.read
80
+ input.seek(original_pos)
81
+ else
82
+ # Non-seekable IO - read and consume
83
+ content = input.read
84
+ end
85
+ content
86
+ elsif input.is_a?(String)
87
+ # Could be file path or raw data
88
+ # If string contains null byte, it's definitely data (not a path)
89
+ # Also check if it's a valid file path first
90
+ if !input.include?("\0") && File.exist?(input)
91
+ File.binread(input)
92
+ else
93
+ input.b
94
+ end
95
+ else
96
+ raise ArgumentError,
97
+ "Input must be a String or IO object"
98
+ end
99
+
100
+ # Detect format and decode
101
+ decompressed = decode_lzma_data(data)
102
+
103
+ if output
104
+ if output.respond_to?(:write)
105
+ output.write(decompressed)
106
+ else
107
+ File.binwrite(output, decompressed)
108
+ end
109
+ nil
110
+ else
111
+ decompressed
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ # Decode LZMA-based data by detecting format
118
+ # @param data [String] Input data
119
+ # @return [String] Decompressed data
120
+ def decode_lzma_data(data)
121
+ format = detect_lzma_format(data)
122
+
123
+ case format
124
+ when :lz
125
+ decode_lzip(data)
126
+ when :lzma_alone
127
+ decode_lzma_alone(data)
128
+ when :xz
129
+ decode_xz_stream(data)
130
+ else
131
+ raise FormatError, "Unknown LZMA format: cannot detect valid format"
132
+ end
133
+ end
134
+
135
+ # Detect LZMA format from magic bytes
136
+ # @param data [String] Input data
137
+ # @return [Symbol] Format type (:xz, :lz, :lzma_alone)
138
+ def detect_lzma_format(data)
139
+ return :unknown if data.nil? || data.bytesize < 4
140
+
141
+ first_bytes = data.byteslice(0, 6).bytes.to_a
142
+
143
+ # Check XZ magic: FD 37 7A 58 5A 00
144
+ # Reference: xz-file-format-1.2.1.txt Section 2.1.1.1
145
+ if first_bytes[0] == 0xFD && first_bytes[1] == 0x37 &&
146
+ first_bytes[2] == 0x7A && first_bytes[3] == 0x58 &&
147
+ first_bytes[4] == 0x5A && first_bytes[5].zero?
148
+ return :xz
149
+ end
150
+
151
+ # Check LZIP magic: 4C 5A 49 50 ("LZIP")
152
+ # Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/lzip_decoder.c:106
153
+ if first_bytes[0] == 0x4C && first_bytes[1] == 0x5A &&
154
+ first_bytes[2] == 0x49 && first_bytes[3] == 0x50
155
+ return :lz
156
+ end
157
+
158
+ # Default to LZMA_Alone (legacy format, no magic bytes)
159
+ # The format starts with properties byte, dictionary size, and uncompressed size
160
+ # Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/alone_decoder.c
161
+ :lzma_alone
162
+ end
163
+
164
+ # Decode LZIP format (.lz files)
165
+ # @param data [String] Input data
166
+ # @return [String] Decompressed data
167
+ def decode_lzip(data)
168
+ input = StringIO.new(data)
169
+ decoder = Omnizip::Algorithms::LZMA::LzipDecoder.new(input)
170
+ decoder.decode_stream
171
+ rescue StandardError => e
172
+ raise FormatError, "Failed to decode LZIP format: #{e.message}"
173
+ end
174
+
175
+ # Decode LZMA_Alone format (.lzma files)
176
+ # @param data [String] Input data
177
+ # @return [String] Decompressed data
178
+ def decode_lzma_alone(data)
179
+ input = StringIO.new(data)
180
+ decoder = Omnizip::Algorithms::LZMA::LzmaAloneDecoder.new(input)
181
+ decoder.decode_stream
182
+ rescue StandardError => e
183
+ raise FormatError, "Failed to decode LZMA_Alone format: #{e.message}"
184
+ end
185
+
186
+ # Decode XZ format stream
187
+ # @param data [String] Input data
188
+ # @return [String] Decompressed data
189
+ def decode_xz_stream(data)
190
+ input_io = StringIO.new(data.b)
191
+ reader = Reader.new(input_io)
192
+ reader.read
193
+ rescue StandardError => e
194
+ raise FormatError, "Failed to decode XZ format: #{e.message}"
195
+ end
196
+
197
+ # Alias for compatibility
198
+ alias decode decompress
199
+ alias extract decompress
200
+
201
+ # Entry method for archive-like interface
202
+ # @param input [String, IO] Input XZ data or file path
203
+ # @param options [Hash] Options (reserved)
204
+ # @yield [Entry] Yields entry (XZ has single data stream)
205
+ # @return [String, Entry] Decompressed data or Entry if no block given
206
+ def extract_entry(input, options = {})
207
+ data = decompress(input, nil, options)
208
+
209
+ entry = Entry.new(data)
210
+ if block_given?
211
+ yield entry
212
+ else
213
+ entry
214
+ end
215
+ end
216
+ end
217
+
218
+ # Builder class for convenient file creation
219
+ class Builder
220
+ attr_reader :data
221
+
222
+ def initialize(_options = {})
223
+ @data = String.new(encoding: Encoding::BINARY)
224
+ end
225
+
226
+ def add_data(content)
227
+ @data << content.to_s.dup.force_encoding(Encoding::BINARY)
228
+ end
229
+
230
+ def add_file(path)
231
+ content = File.binread(path)
232
+ add_data(content)
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end