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,251 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module Buffer
5
+ # Wrapper for in-memory archive operations
6
+ #
7
+ # Provides unified interface for adding entries to and reading entries
8
+ # from archives stored in memory (StringIO). Works with both OutputStream
9
+ # (for creating) and InputStream (for reading).
10
+ #
11
+ # @example Creating an archive
12
+ # buffer = StringIO.new
13
+ # Omnizip::Zip::OutputStream.open(buffer) do |zos|
14
+ # archive = MemoryArchive.new(zos, :zip)
15
+ # archive.add('file.txt', 'content')
16
+ # end
17
+ #
18
+ # @example Reading an archive
19
+ # Omnizip::Zip::InputStream.open(buffer) do |zis|
20
+ # archive = MemoryArchive.new(zis, :zip)
21
+ # archive.each_entry do |entry|
22
+ # puts entry.name
23
+ # end
24
+ # end
25
+ class MemoryArchive
26
+ attr_reader :format, :stream
27
+
28
+ # Initialize memory archive wrapper
29
+ #
30
+ # @param stream [Omnizip::Zip::OutputStream, Omnizip::Zip::InputStream]
31
+ # Underlying stream
32
+ # @param format [Symbol] Archive format (:zip, :seven_zip)
33
+ def initialize(stream, format)
34
+ @stream = stream
35
+ @format = format
36
+ @entries_cache = nil
37
+ end
38
+
39
+ # Add file from memory (write mode only)
40
+ #
41
+ # @param name [String] Entry name (path within archive)
42
+ # @param data [String] Entry content
43
+ # @param options [Hash] Entry options
44
+ # @option options [Time] :time Modification time (default: now)
45
+ # @option options [String] :comment Entry comment
46
+ # @option options [Symbol] :compression Compression method
47
+ # (:store, :deflate)
48
+ # @option options [Integer] :level Compression level (1-9)
49
+ # @return [self] For method chaining
50
+ #
51
+ # @example Add multiple files
52
+ # archive.add('file1.txt', 'content1')
53
+ # .add('file2.txt', 'content2')
54
+ # .add('dir/', '') # Directory entry
55
+ #
56
+ # @raise [RuntimeError] If stream is not an OutputStream
57
+ def add(name, data, **options)
58
+ ensure_write_mode!
59
+
60
+ case stream
61
+ when Omnizip::Zip::OutputStream
62
+ stream.put_next_entry(name, **options)
63
+ stream.write(data) unless name.end_with?("/")
64
+ else
65
+ raise NotImplementedError,
66
+ "Unsupported stream type: #{stream.class}"
67
+ end
68
+
69
+ self
70
+ end
71
+
72
+ # Add data with block (write mode only)
73
+ #
74
+ # @param name [String] Entry name
75
+ # @param options [Hash] Entry options
76
+ # @yield Block that returns content
77
+ # @yieldreturn [String] Entry content
78
+ # @return [self] For method chaining
79
+ #
80
+ # @example Add with block
81
+ # archive.add_data('file.txt') { File.read('source.txt') }
82
+ def add_data(name, **options)
83
+ ensure_write_mode!
84
+ data = yield
85
+ add(name, data, **options)
86
+ end
87
+
88
+ # Iterate entries (read mode only)
89
+ #
90
+ # @yield [entry] Block called for each entry
91
+ # @yieldparam entry [Entry] Archive entry
92
+ # @return [void]
93
+ #
94
+ # @example Process all entries
95
+ # archive.each_entry do |entry|
96
+ # puts "#{entry.name}: #{entry.size} bytes"
97
+ # content = entry.read unless entry.directory?
98
+ # end
99
+ #
100
+ # @raise [RuntimeError] If stream is not an InputStream
101
+ def each_entry
102
+ ensure_read_mode!
103
+
104
+ case stream
105
+ when Omnizip::Zip::InputStream
106
+ while (zip_entry = stream.get_next_entry)
107
+ entry = Entry.new(zip_entry, stream)
108
+ yield(entry)
109
+ end
110
+ else
111
+ raise NotImplementedError,
112
+ "Unsupported stream type: #{stream.class}"
113
+ end
114
+ end
115
+
116
+ # Extract all entries to memory (read mode only)
117
+ #
118
+ # @return [Hash<String, String>] Filename => content mapping
119
+ #
120
+ # @example Extract all
121
+ # files = archive.extract_all_to_memory
122
+ # files.each { |name, content| puts "#{name}: #{content.size}" }
123
+ def extract_all_to_memory
124
+ ensure_read_mode!
125
+
126
+ result = {}
127
+ each_entry do |entry|
128
+ result[entry.name] = entry.read unless entry.directory?
129
+ end
130
+ result
131
+ end
132
+
133
+ # Get underlying buffer as string (write mode only)
134
+ #
135
+ # @return [String] Complete archive as binary string
136
+ #
137
+ # @example Get archive data
138
+ # archive_data = archive.to_s
139
+ # File.binwrite('output.zip', archive_data)
140
+ #
141
+ # @raise [RuntimeError] If stream is not an OutputStream
142
+ def to_s
143
+ ensure_write_mode!
144
+
145
+ case stream
146
+ when Omnizip::Zip::OutputStream
147
+ # OutputStream wraps the IO, we need to get the underlying buffer
148
+ # This is only safe after close
149
+ unless stream.closed?
150
+ raise "Archive must be closed before accessing data"
151
+ end
152
+
153
+ # The buffer was passed in during creation, but we don't have
154
+ # direct access. This method should be called on the StringIO
155
+ # returned by Buffer.create instead.
156
+ raise NotImplementedError,
157
+ "Use Buffer.create return value instead"
158
+ else
159
+ raise "Cannot get string from read mode archive"
160
+ end
161
+ end
162
+
163
+ # Entry wrapper with read capability
164
+ #
165
+ # Wraps underlying ZIP entry to provide consistent interface
166
+ # for reading entry data from the stream.
167
+ class Entry
168
+ attr_reader :name, :size, :compressed_size, :time, :comment
169
+
170
+ # Initialize entry wrapper
171
+ #
172
+ # @param entry [Omnizip::Zip::Entry] Underlying entry
173
+ # @param stream [Omnizip::Zip::InputStream] Stream to read from
174
+ def initialize(entry, stream)
175
+ @entry = entry
176
+ @stream = stream
177
+ @name = entry.name
178
+ @size = entry.size
179
+ @compressed_size = entry.compressed_size
180
+ @time = entry.time
181
+ @comment = entry.comment
182
+ @directory = entry.directory?
183
+ end
184
+
185
+ # Read entry content
186
+ #
187
+ # @param size [Integer, nil] Number of bytes to read (nil for all)
188
+ # @return [String, nil] Entry data or nil if EOF
189
+ #
190
+ # @example Read entire entry
191
+ # content = entry.read
192
+ #
193
+ # @example Read in chunks
194
+ # while (chunk = entry.read(8192))
195
+ # process_chunk(chunk)
196
+ # end
197
+ def read(size = nil)
198
+ @stream.read(size)
199
+ end
200
+
201
+ # Check if entry is a directory
202
+ #
203
+ # @return [Boolean] True if directory entry
204
+ def directory?
205
+ @directory
206
+ end
207
+
208
+ # Check if entry is a file
209
+ #
210
+ # @return [Boolean] True if file entry
211
+ def file?
212
+ !@directory
213
+ end
214
+
215
+ # Get compression method
216
+ #
217
+ # @return [Symbol] Compression method (:store, :deflate, etc.)
218
+ def compression_method
219
+ @entry.compression_method
220
+ end
221
+
222
+ # Get CRC32 checksum
223
+ #
224
+ # @return [Integer] CRC32 value
225
+ def crc32
226
+ @entry.crc32
227
+ end
228
+ end
229
+
230
+ private
231
+
232
+ # Ensure stream is in write mode (OutputStream)
233
+ #
234
+ # @raise [RuntimeError] If not in write mode
235
+ def ensure_write_mode!
236
+ return if stream.is_a?(Omnizip::Zip::OutputStream)
237
+
238
+ raise "Operation requires write mode (OutputStream)"
239
+ end
240
+
241
+ # Ensure stream is in read mode (InputStream)
242
+ #
243
+ # @raise [RuntimeError] If not in read mode
244
+ def ensure_read_mode!
245
+ return if stream.is_a?(Omnizip::Zip::InputStream)
246
+
247
+ raise "Operation requires read mode (InputStream)"
248
+ end
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module Omnizip
6
+ module Buffer
7
+ # Extract archive contents to memory
8
+ #
9
+ # Provides efficient extraction of archive entries to Hash without
10
+ # loading all files at once. Uses lazy evaluation where possible.
11
+ #
12
+ # @example Extract all files
13
+ # extractor = MemoryExtractor.new(zip_data)
14
+ # files = extractor.extract_all
15
+ # # => {"file1.txt" => "content1", "file2.txt" => "content2"}
16
+ #
17
+ # @example Extract single file
18
+ # extractor = MemoryExtractor.new(zip_data)
19
+ # content = extractor.extract_entry('file1.txt')
20
+ # # => "content1"
21
+ class MemoryExtractor
22
+ attr_reader :format
23
+
24
+ # Initialize extractor
25
+ #
26
+ # @param data [String, StringIO] Archive data
27
+ # @param format [Symbol, nil] Archive format (auto-detected if nil)
28
+ #
29
+ # @example Create extractor
30
+ # extractor = MemoryExtractor.new(zip_data)
31
+ # extractor = MemoryExtractor.new(zip_buffer, format: :zip)
32
+ def initialize(data, format: nil)
33
+ @buffer = data.is_a?(StringIO) ? data : StringIO.new(data.b)
34
+ @format = format || detect_format
35
+ @extracted_cache = {}
36
+ end
37
+
38
+ # Extract all entries to Hash
39
+ #
40
+ # @return [Hash<String, String>] Filename => content mapping
41
+ #
42
+ # @example Extract everything
43
+ # files = extractor.extract_all
44
+ # files.keys # => ["file1.txt", "file2.txt", "dir/file3.txt"]
45
+ def extract_all
46
+ result = {}
47
+
48
+ case @format
49
+ when :zip
50
+ extract_all_zip(result)
51
+ when :seven_zip, :'7z'
52
+ raise NotImplementedError, "7z format support coming in Phase 2"
53
+ else
54
+ raise ArgumentError, "Unsupported format: #{@format}"
55
+ end
56
+
57
+ result
58
+ end
59
+
60
+ # Extract single entry by name
61
+ #
62
+ # @param name [String] Entry name to extract
63
+ # @return [String, nil] Entry content or nil if not found
64
+ #
65
+ # @example Extract specific file
66
+ # content = extractor.extract_entry('readme.txt')
67
+ # # => "Hello World"
68
+ def extract_entry(name)
69
+ # Check cache first
70
+ return @extracted_cache[name] if @extracted_cache.key?(name)
71
+
72
+ # Extract from archive
73
+ content = nil
74
+
75
+ case @format
76
+ when :zip
77
+ content = extract_entry_zip(name)
78
+ when :seven_zip, :'7z'
79
+ raise NotImplementedError, "7z format support coming in Phase 2"
80
+ else
81
+ raise ArgumentError, "Unsupported format: #{@format}"
82
+ end
83
+
84
+ # Cache the result
85
+ @extracted_cache[name] = content if content
86
+ content
87
+ end
88
+
89
+ # List all entry names without extracting
90
+ #
91
+ # @return [Array<String>] Entry names
92
+ #
93
+ # @example List files
94
+ # extractor.list_entries
95
+ # # => ["file1.txt", "dir/", "dir/file2.txt"]
96
+ def list_entries
97
+ names = []
98
+
99
+ case @format
100
+ when :zip
101
+ list_entries_zip(names)
102
+ when :seven_zip, :'7z'
103
+ raise NotImplementedError, "7z format support coming in Phase 2"
104
+ else
105
+ raise ArgumentError, "Unsupported format: #{@format}"
106
+ end
107
+
108
+ names
109
+ end
110
+
111
+ # Check if entry exists in archive
112
+ #
113
+ # @param name [String] Entry name
114
+ # @return [Boolean] True if entry exists
115
+ #
116
+ # @example Check existence
117
+ # extractor.entry_exists?('file.txt') # => true
118
+ def entry_exists?(name)
119
+ list_entries.include?(name)
120
+ end
121
+
122
+ # Get total number of entries
123
+ #
124
+ # @return [Integer] Number of entries
125
+ def entry_count
126
+ list_entries.size
127
+ end
128
+
129
+ # Extract entries matching pattern
130
+ #
131
+ # @param pattern [Regexp, String] Pattern to match
132
+ # @return [Hash<String, String>] Matching entries
133
+ #
134
+ # @example Extract by pattern
135
+ # extractor.extract_matching(/\.txt$/)
136
+ # # => {"file1.txt" => "content1", "file2.txt" => "content2"}
137
+ def extract_matching(pattern)
138
+ pattern = Regexp.new(pattern) if pattern.is_a?(String)
139
+ result = {}
140
+
141
+ list_entries.each do |name|
142
+ next unless name&.match?(pattern)
143
+ next if name.end_with?("/") # Skip directories
144
+
145
+ content = extract_entry(name)
146
+ result[name] = content if content
147
+ end
148
+
149
+ result
150
+ end
151
+
152
+ private
153
+
154
+ # Detect archive format from magic bytes
155
+ #
156
+ # @return [Symbol] Detected format
157
+ # @raise [Omnizip::FormatError] If format cannot be detected
158
+ def detect_format
159
+ magic = @buffer.read(4)
160
+ @buffer.rewind
161
+
162
+ case magic
163
+ when "PK\x03\x04", "PK\x05\x06", "PK\x07\x08"
164
+ # ZIP signatures: local file header, EOCD, data descriptor
165
+ :zip
166
+ when "7z\xBC\xAF"
167
+ :seven_zip
168
+ else
169
+ raise Omnizip::FormatError,
170
+ "Unknown archive format (magic: #{magic.inspect})"
171
+ end
172
+ end
173
+
174
+ # Extract all entries from ZIP
175
+ #
176
+ # @param result [Hash] Hash to populate with entries
177
+ def extract_all_zip(result)
178
+ @buffer.rewind
179
+ Omnizip::Zip::InputStream.open(@buffer) do |zis|
180
+ while (entry = zis.get_next_entry)
181
+ next if entry.directory?
182
+
183
+ content = zis.read
184
+ result[entry.name] = content
185
+ @extracted_cache[entry.name] = content
186
+ end
187
+ end
188
+ end
189
+
190
+ # Extract single entry from ZIP
191
+ #
192
+ # @param name [String] Entry name
193
+ # @return [String, nil] Entry content or nil if not found
194
+ def extract_entry_zip(name)
195
+ @buffer.rewind
196
+ content = nil
197
+
198
+ Omnizip::Zip::InputStream.open(@buffer) do |zis|
199
+ while (entry = zis.get_next_entry)
200
+ if entry.name == name
201
+ content = zis.read unless entry.directory?
202
+ break
203
+ end
204
+ end
205
+ end
206
+
207
+ content
208
+ end
209
+
210
+ # List all entry names from ZIP
211
+ #
212
+ # @param names [Array] Array to populate with names
213
+ def list_entries_zip(names)
214
+ @buffer.rewind
215
+
216
+ Omnizip::Zip::InputStream.open(@buffer) do |zis|
217
+ while (entry = zis.get_next_entry)
218
+ names << entry.name
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+ require_relative "buffer/memory_archive"
5
+ require_relative "buffer/memory_extractor"
6
+
7
+ module Omnizip
8
+ # In-memory archive operations without filesystem I/O
9
+ #
10
+ # This module provides methods for creating and reading archives
11
+ # entirely in memory using StringIO, enabling web applications,
12
+ # testing, and API responses without temporary files.
13
+ #
14
+ # @example Create archive in memory
15
+ # zip_data = Omnizip::Buffer.create(:zip) do |archive|
16
+ # archive.add('readme.txt', 'Hello World')
17
+ # archive.add('data.json', '{"key": "value"}')
18
+ # end
19
+ # # => Returns StringIO with complete ZIP archive
20
+ #
21
+ # @example Extract from memory
22
+ # contents = Omnizip::Buffer.extract_to_memory(zip_data)
23
+ # # => {"readme.txt" => "Hello World", "data.json" => '{"key": "value"}'}
24
+ #
25
+ # @example From Hash
26
+ # archive_data = {
27
+ # 'file1.txt' => 'content1',
28
+ # 'file2.txt' => 'content2'
29
+ # }
30
+ # zip_buffer = Omnizip::Buffer.create_from_hash(archive_data, :zip)
31
+ module Buffer
32
+ class << self
33
+ # Create archive in memory
34
+ #
35
+ # @param format [Symbol] Archive format (:zip, :seven_zip)
36
+ # @param options [Hash] Format-specific options
37
+ # @yield [archive] Block to populate archive
38
+ # @yieldparam archive [MemoryArchive] Archive object to add entries to
39
+ # @return [StringIO] Complete archive in memory, rewound to start
40
+ #
41
+ # @example Create ZIP in memory
42
+ # buffer = Omnizip::Buffer.create(:zip) do |archive|
43
+ # archive.add('file.txt', 'content')
44
+ # archive.add('dir/file2.txt', 'more content')
45
+ # end
46
+ # File.binwrite('output.zip', buffer.string)
47
+ # rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
48
+ def create(format = :zip, **options, &block)
49
+ buffer = StringIO.new(String.new(encoding: Encoding::BINARY))
50
+
51
+ case format
52
+ when :zip
53
+ create_zip(buffer, options, &block)
54
+ when :seven_zip, :'7z'
55
+ raise NotImplementedError, "7z format support coming in Phase 2"
56
+ else
57
+ raise ArgumentError, "Unsupported format: #{format}"
58
+ end
59
+
60
+ buffer.tap(&:rewind)
61
+ end
62
+ # rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding
63
+
64
+ # Open archive from memory
65
+ #
66
+ # @param data [String, StringIO] Archive data
67
+ # @param format [Symbol, nil] Archive format (auto-detected if nil)
68
+ # @yield [archive] Block to read from archive
69
+ # @yieldparam archive [MemoryArchive] Archive object to read entries from
70
+ # @return [MemoryArchive, Object] Archive object or block return value
71
+ #
72
+ # @example Read entries
73
+ # Omnizip::Buffer.open(zip_data) do |archive|
74
+ # archive.each_entry do |entry|
75
+ # puts "#{entry.name}: #{entry.size} bytes"
76
+ # end
77
+ # end
78
+ def open(data, format: nil, &block)
79
+ buffer = data.is_a?(StringIO) ? data : StringIO.new(data.b)
80
+ format ||= detect_format(buffer)
81
+
82
+ case format
83
+ when :zip
84
+ open_zip(buffer, &block)
85
+ when :seven_zip, :'7z'
86
+ raise NotImplementedError, "7z format support coming in Phase 2"
87
+ else
88
+ raise ArgumentError, "Unsupported format: #{format}"
89
+ end
90
+ end
91
+
92
+ # Extract all entries to memory
93
+ #
94
+ # @param data [String, StringIO] Archive data
95
+ # @param format [Symbol, nil] Archive format (auto-detected if nil)
96
+ # @return [Hash<String, String>] Filename => content mapping
97
+ #
98
+ # @example Extract to Hash
99
+ # files = Omnizip::Buffer.extract_to_memory(zip_data)
100
+ # files.each do |name, content|
101
+ # puts "#{name}: #{content.bytesize} bytes"
102
+ # end
103
+ def extract_to_memory(data, format: nil)
104
+ extractor = Buffer::MemoryExtractor.new(data, format: format)
105
+ extractor.extract_all
106
+ end
107
+
108
+ # Create archive from Hash of filename => content
109
+ #
110
+ # @param hash [Hash<String, String>] Filename => content mapping
111
+ # @param format [Symbol] Archive format
112
+ # @param options [Hash] Format-specific options
113
+ # @return [StringIO] Complete archive in memory
114
+ #
115
+ # @example Create from Hash
116
+ # data = {'file1.txt' => 'content1', 'file2.txt' => 'content2'}
117
+ # zip = Omnizip::Buffer.create_from_hash(data, :zip)
118
+ def create_from_hash(hash, format = :zip, **options)
119
+ create(format, **options) do |archive|
120
+ hash.each do |name, content|
121
+ archive.add(name, content)
122
+ end
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ # Detect archive format from magic bytes
129
+ #
130
+ # @param buffer [StringIO] Buffer containing archive data
131
+ # @return [Symbol] Detected format
132
+ # @raise [Omnizip::FormatError] If format cannot be detected
133
+ def detect_format(buffer)
134
+ magic = buffer.read(4)
135
+ buffer.rewind
136
+
137
+ case magic
138
+ when "PK\x03\x04", "PK\x05\x06", "PK\x07\x08"
139
+ # ZIP signatures: local file header, EOCD, data descriptor
140
+ :zip
141
+ when "7z\xBC\xAF"
142
+ :seven_zip
143
+ else
144
+ raise Omnizip::FormatError,
145
+ "Unknown archive format (magic: #{magic.inspect})"
146
+ end
147
+ end
148
+
149
+ # Create ZIP archive in buffer
150
+ #
151
+ # @param buffer [StringIO] Buffer to write to
152
+ # @param options [Hash] ZIP-specific options
153
+ # @yield [archive] Block to populate archive
154
+ def create_zip(buffer, _options, &block)
155
+ Omnizip::Zip::OutputStream.open(buffer) do |zos|
156
+ archive = Buffer::MemoryArchive.new(zos, :zip)
157
+ block&.call(archive)
158
+ end
159
+ end
160
+
161
+ # Open ZIP archive from buffer
162
+ #
163
+ # @param buffer [StringIO] Buffer containing ZIP data
164
+ # @yield [archive] Block to read from archive
165
+ # @return [MemoryArchive, Object] Archive or block return value
166
+ def open_zip(buffer, &block)
167
+ result = nil
168
+ Omnizip::Zip::InputStream.open(buffer) do |zis|
169
+ archive = Buffer::MemoryArchive.new(zis, :zip)
170
+ result = block ? yield(archive) : archive
171
+ end
172
+ result
173
+ end
174
+ end
175
+ end
176
+ end