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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +754 -0
- data/COPYING +502 -0
- data/Gemfile +17 -0
- data/LICENSE +12 -0
- data/README.adoc +1045 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +91 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +271 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +185 -0
- data/docs/getting-started/your-first-archive.adoc +218 -0
- data/docs/guides/advanced-features/encryption.adoc +300 -0
- data/docs/guides/advanced-features/index.adoc +49 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
- data/docs/guides/advanced-features/streaming.adoc +212 -0
- data/docs/guides/archive-formats/gzip-format.adoc +107 -0
- data/docs/guides/archive-formats/index.adoc +130 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
- data/docs/guides/compression-algorithms/deflate.adoc +319 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +398 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
- data/docs/guides/creating-archives.adoc +354 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +218 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +188 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/troubleshooting/index.adoc +208 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +130 -0
- data/lib/omnizip/algorithm_registry.rb +86 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
- data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
- data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
- data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
- data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
- data/lib/omnizip/algorithms/bzip2.rb +130 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
- data/lib/omnizip/algorithms/deflate.rb +128 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
- data/lib/omnizip/algorithms/deflate64.rb +109 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
- data/lib/omnizip/algorithms/lzma.rb +238 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +141 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +116 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +82 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
- data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
- data/lib/omnizip/algorithms/zstandard.rb +138 -0
- data/lib/omnizip/buffer/memory_archive.rb +251 -0
- data/lib/omnizip/buffer/memory_extractor.rb +224 -0
- data/lib/omnizip/buffer.rb +176 -0
- data/lib/omnizip/checksum_registry.rb +114 -0
- data/lib/omnizip/checksums/crc32.rb +100 -0
- data/lib/omnizip/checksums/crc64.rb +101 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/verifier.rb +131 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +120 -0
- data/lib/omnizip/chunked.rb +129 -0
- data/lib/omnizip/cli/output_formatter.rb +104 -0
- data/lib/omnizip/cli.rb +572 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +427 -0
- data/lib/omnizip/commands/archive_extract_command.rb +272 -0
- data/lib/omnizip/commands/archive_list_command.rb +218 -0
- data/lib/omnizip/commands/archive_repair_command.rb +131 -0
- data/lib/omnizip/commands/archive_verify_command.rb +117 -0
- data/lib/omnizip/commands/compress_command.rb +117 -0
- data/lib/omnizip/commands/decompress_command.rb +120 -0
- data/lib/omnizip/commands/list_command.rb +53 -0
- data/lib/omnizip/commands/metadata_command.rb +153 -0
- data/lib/omnizip/commands/parity_create_command.rb +122 -0
- data/lib/omnizip/commands/parity_repair_command.rb +122 -0
- data/lib/omnizip/commands/parity_verify_command.rb +124 -0
- data/lib/omnizip/commands/profile_list_command.rb +56 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/convenience.rb +359 -0
- data/lib/omnizip/converter/conversion_registry.rb +49 -0
- data/lib/omnizip/converter/conversion_strategy.rb +121 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
- data/lib/omnizip/converter.rb +105 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/error.rb +106 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +63 -0
- data/lib/omnizip/extraction/filter_chain.rb +177 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +240 -0
- data/lib/omnizip/extraction.rb +111 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +113 -0
- data/lib/omnizip/filter.rb +139 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +166 -0
- data/lib/omnizip/filters/bcj.rb +279 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
- data/lib/omnizip/filters/bcj2.rb +99 -0
- data/lib/omnizip/filters/bcj_arm.rb +176 -0
- data/lib/omnizip/filters/bcj_arm64.rb +244 -0
- data/lib/omnizip/filters/bcj_ia64.rb +196 -0
- data/lib/omnizip/filters/bcj_ppc.rb +190 -0
- data/lib/omnizip/filters/bcj_sparc.rb +176 -0
- data/lib/omnizip/filters/bcj_x86.rb +193 -0
- data/lib/omnizip/filters/delta.rb +196 -0
- data/lib/omnizip/filters/filter_base.rb +72 -0
- data/lib/omnizip/filters/registry.rb +123 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/format_detector.rb +162 -0
- data/lib/omnizip/format_registry.rb +59 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +172 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +385 -0
- data/lib/omnizip/formats/cpio/reader.rb +196 -0
- data/lib/omnizip/formats/cpio/writer.rb +234 -0
- data/lib/omnizip/formats/cpio.rb +140 -0
- data/lib/omnizip/formats/format_spec_loader.rb +230 -0
- data/lib/omnizip/formats/gzip.rb +238 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
- data/lib/omnizip/formats/iso/directory_record.rb +152 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +197 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +530 -0
- data/lib/omnizip/formats/iso.rb +140 -0
- data/lib/omnizip/formats/lzip.rb +175 -0
- data/lib/omnizip/formats/lzma_alone.rb +171 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
- data/lib/omnizip/formats/rar/block_parser.rb +243 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +82 -0
- data/lib/omnizip/formats/rar/decompressor.rb +238 -0
- data/lib/omnizip/formats/rar/external_writer.rb +312 -0
- data/lib/omnizip/formats/rar/header.rb +192 -0
- data/lib/omnizip/formats/rar/license_validator.rb +109 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
- data/lib/omnizip/formats/rar/reader.rb +366 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
- data/lib/omnizip/formats/rar/writer.rb +431 -0
- data/lib/omnizip/formats/rar.rb +205 -0
- data/lib/omnizip/formats/rar3/compressor.rb +73 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar3/reader.rb +386 -0
- data/lib/omnizip/formats/rar3/writer.rb +219 -0
- data/lib/omnizip/formats/rar5/compressor.rb +73 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar5/reader.rb +342 -0
- data/lib/omnizip/formats/rar5/writer.rb +214 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
- data/lib/omnizip/formats/seven_zip/header.rb +106 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
- data/lib/omnizip/formats/seven_zip.rb +93 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +94 -0
- data/lib/omnizip/formats/tar/header.rb +168 -0
- data/lib/omnizip/formats/tar/reader.rb +121 -0
- data/lib/omnizip/formats/tar/writer.rb +216 -0
- data/lib/omnizip/formats/tar.rb +84 -0
- data/lib/omnizip/formats/xz/reader.rb +116 -0
- data/lib/omnizip/formats/xz.rb +237 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
- data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
- data/lib/omnizip/formats/zip/reader.rb +250 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +375 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
- data/lib/omnizip/io/buffered_input.rb +146 -0
- data/lib/omnizip/io/buffered_output.rb +105 -0
- data/lib/omnizip/io/stream_manager.rb +115 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +124 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +146 -0
- data/lib/omnizip/metadata/metadata_editor.rb +171 -0
- data/lib/omnizip/metadata/metadata_registry.rb +64 -0
- data/lib/omnizip/metadata/metadata_validator.rb +99 -0
- data/lib/omnizip/metadata.rb +57 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +73 -0
- data/lib/omnizip/models/compression_options.rb +71 -0
- data/lib/omnizip/models/conversion_options.rb +87 -0
- data/lib/omnizip/models/conversion_result.rb +135 -0
- data/lib/omnizip/models/eta_result.rb +46 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +144 -0
- data/lib/omnizip/models/filter_config.rb +183 -0
- data/lib/omnizip/models/match_result.rb +124 -0
- data/lib/omnizip/models/optimization_suggestion.rb +91 -0
- data/lib/omnizip/models/parallel_options.rb +104 -0
- data/lib/omnizip/models/performance_result.rb +79 -0
- data/lib/omnizip/models/profile_report.rb +82 -0
- data/lib/omnizip/models/progress_options.rb +38 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +81 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +203 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
- data/lib/omnizip/parallel/worker_pool.rb +223 -0
- data/lib/omnizip/parallel.rb +149 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +73 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
- data/lib/omnizip/parity/models/main_packet.rb +128 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +109 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
- data/lib/omnizip/parity/par2_creator.rb +531 -0
- data/lib/omnizip/parity/par2_repairer.rb +407 -0
- data/lib/omnizip/parity/par2_verifier.rb +364 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
- data/lib/omnizip/parity.rb +186 -0
- data/lib/omnizip/password/encryption_registry.rb +65 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
- data/lib/omnizip/password.rb +87 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
- data/lib/omnizip/pipe.rb +121 -0
- data/lib/omnizip/platform/ntfs_streams.rb +201 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +39 -0
- data/lib/omnizip/profile/balanced_profile.rb +33 -0
- data/lib/omnizip/profile/binary_profile.rb +36 -0
- data/lib/omnizip/profile/compression_profile.rb +158 -0
- data/lib/omnizip/profile/custom_profile.rb +157 -0
- data/lib/omnizip/profile/fast_profile.rb +33 -0
- data/lib/omnizip/profile/maximum_profile.rb +33 -0
- data/lib/omnizip/profile/profile_detector.rb +110 -0
- data/lib/omnizip/profile/profile_registry.rb +161 -0
- data/lib/omnizip/profile/text_profile.rb +36 -0
- data/lib/omnizip/profile.rb +190 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +169 -0
- data/lib/omnizip/profiler.rb +204 -0
- data/lib/omnizip/progress/callback_reporter.rb +36 -0
- data/lib/omnizip/progress/console_reporter.rb +62 -0
- data/lib/omnizip/progress/log_reporter.rb +91 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +190 -0
- data/lib/omnizip/progress/silent_reporter.rb +24 -0
- data/lib/omnizip/progress.rb +127 -0
- data/lib/omnizip/rubyzip_compat.rb +63 -0
- data/lib/omnizip/temp/safe_extract.rb +168 -0
- data/lib/omnizip/temp/temp_file.rb +124 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +181 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +156 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +273 -0
- data/lib/omnizip/zip/output_stream.rb +324 -0
- data/lib/omnizip.rb +156 -0
- data/readme-docs/advanced-features.adoc +515 -0
- data/readme-docs/api-usage.adoc +444 -0
- data/readme-docs/architecture.adoc +449 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +442 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +325 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- 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
|