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,315 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "constants"
|
|
4
|
+
require_relative "file_collector"
|
|
5
|
+
require_relative "stream_compressor"
|
|
6
|
+
require_relative "header_writer"
|
|
7
|
+
require_relative "models/file_entry"
|
|
8
|
+
require_relative "../../models/split_options"
|
|
9
|
+
|
|
10
|
+
module Omnizip
|
|
11
|
+
module Formats
|
|
12
|
+
module SevenZip
|
|
13
|
+
# Split archive writer for .7z format
|
|
14
|
+
# Creates multi-volume archives with size limits
|
|
15
|
+
class SplitArchiveWriter
|
|
16
|
+
include Constants
|
|
17
|
+
|
|
18
|
+
attr_reader :base_path, :options, :split_options, :entries, :volumes
|
|
19
|
+
|
|
20
|
+
# Volume information
|
|
21
|
+
class VolumeInfo
|
|
22
|
+
attr_accessor :path, :size, :start_offset, :end_offset
|
|
23
|
+
|
|
24
|
+
def initialize(path, start_offset = 0)
|
|
25
|
+
@path = path
|
|
26
|
+
@size = 0
|
|
27
|
+
@start_offset = start_offset
|
|
28
|
+
@end_offset = start_offset
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Initialize writer
|
|
33
|
+
#
|
|
34
|
+
# @param base_path [String] Base path (e.g., "backup.7z.001")
|
|
35
|
+
# @param options [Hash] Compression options
|
|
36
|
+
# @param split_options [Models::SplitOptions] Split configuration
|
|
37
|
+
def initialize(base_path, options = {}, split_options = nil)
|
|
38
|
+
@base_path = base_path
|
|
39
|
+
@options = {
|
|
40
|
+
algorithm: :lzma2,
|
|
41
|
+
level: 5,
|
|
42
|
+
solid: true,
|
|
43
|
+
filters: [],
|
|
44
|
+
}.merge(options)
|
|
45
|
+
@split_options = split_options || Models::SplitOptions.new
|
|
46
|
+
@split_options.validate!
|
|
47
|
+
@collector = FileCollector.new
|
|
48
|
+
@entries = []
|
|
49
|
+
@volumes = []
|
|
50
|
+
@current_volume = nil
|
|
51
|
+
@current_volume_number = 1
|
|
52
|
+
@global_offset = 0
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Add file to archive
|
|
56
|
+
#
|
|
57
|
+
# @param file_path [String] Path to file
|
|
58
|
+
# @param archive_path [String, nil] Path in archive
|
|
59
|
+
def add_file(file_path, archive_path = nil)
|
|
60
|
+
@collector.add_path(file_path, archive_path: archive_path,
|
|
61
|
+
recursive: false)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Add directory to archive
|
|
65
|
+
#
|
|
66
|
+
# @param dir_path [String] Path to directory
|
|
67
|
+
# @param recursive [Boolean] Add contents recursively
|
|
68
|
+
def add_directory(dir_path, recursive: true)
|
|
69
|
+
@collector.add_path(dir_path, recursive: recursive)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Add files matching glob pattern
|
|
73
|
+
#
|
|
74
|
+
# @param pattern [String] Glob pattern
|
|
75
|
+
def add_files(pattern)
|
|
76
|
+
@collector.add_glob(pattern)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Write split archive
|
|
80
|
+
#
|
|
81
|
+
# @raise [RuntimeError] on write error
|
|
82
|
+
def write
|
|
83
|
+
# Collect files
|
|
84
|
+
@entries = @collector.collect_files
|
|
85
|
+
|
|
86
|
+
# Determine spanning strategy
|
|
87
|
+
if @split_options.span_strategy == Omnizip::Models::SplitOptions::STRATEGY_BALANCED
|
|
88
|
+
write_balanced
|
|
89
|
+
else
|
|
90
|
+
write_first_fit
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
# Write using first-fit strategy (default)
|
|
97
|
+
def write_first_fit
|
|
98
|
+
# Compress all files first
|
|
99
|
+
compressed_result = compress_files
|
|
100
|
+
|
|
101
|
+
# Calculate total size needed
|
|
102
|
+
compressed_result[:data].bytesize
|
|
103
|
+
header_data = build_next_header(compressed_result)
|
|
104
|
+
header_data.bytesize
|
|
105
|
+
|
|
106
|
+
# Create volumes and write data
|
|
107
|
+
start_first_volume
|
|
108
|
+
write_packed_data(compressed_result[:data])
|
|
109
|
+
|
|
110
|
+
# Write header at the end of last volume
|
|
111
|
+
header_offset = @global_offset - START_HEADER_SIZE
|
|
112
|
+
write_data(header_data)
|
|
113
|
+
|
|
114
|
+
# Write start header to first volume (this closes volumes)
|
|
115
|
+
write_start_header_to_first_volume(header_data, header_offset)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Write using balanced strategy
|
|
119
|
+
# Pre-calculates optimal file distribution
|
|
120
|
+
def write_balanced
|
|
121
|
+
# For now, use first-fit strategy
|
|
122
|
+
# Full balanced implementation would require:
|
|
123
|
+
# 1. Calculate individual file sizes
|
|
124
|
+
# 2. Use bin-packing algorithm
|
|
125
|
+
# 3. Distribute files optimally
|
|
126
|
+
write_first_fit
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Compress all files
|
|
130
|
+
#
|
|
131
|
+
# @return [Hash] Compression results
|
|
132
|
+
def compress_files
|
|
133
|
+
compressor = StreamCompressor.new(
|
|
134
|
+
algorithm: @options[:algorithm],
|
|
135
|
+
level: @options[:level],
|
|
136
|
+
filters: @options[:filters],
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
files_with_data = @entries.select(&:has_stream?)
|
|
140
|
+
|
|
141
|
+
if @options[:solid]
|
|
142
|
+
# Solid: compress all files into one stream
|
|
143
|
+
result = compressor.compress_files(files_with_data)
|
|
144
|
+
files_with_data.each_with_index do |entry, i|
|
|
145
|
+
entry.crc = result[:crcs][i]
|
|
146
|
+
entry.size = result[:unpack_sizes][i]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
{
|
|
150
|
+
data: result[:packed_data],
|
|
151
|
+
folders: [{
|
|
152
|
+
method_id: compressor.method_id,
|
|
153
|
+
properties: compressor.properties,
|
|
154
|
+
unpack_size: result[:unpack_size],
|
|
155
|
+
}],
|
|
156
|
+
pack_sizes: [result[:packed_size]],
|
|
157
|
+
unpack_sizes: result[:unpack_sizes],
|
|
158
|
+
digests: result[:crcs],
|
|
159
|
+
}
|
|
160
|
+
else
|
|
161
|
+
# Non-solid: compress each file separately
|
|
162
|
+
packed_data = String.new(encoding: "BINARY")
|
|
163
|
+
folders = []
|
|
164
|
+
pack_sizes = []
|
|
165
|
+
unpack_sizes = []
|
|
166
|
+
digests = []
|
|
167
|
+
|
|
168
|
+
files_with_data.each do |entry|
|
|
169
|
+
data = File.binread(entry.source_path)
|
|
170
|
+
compressed = compressor.compress(data)
|
|
171
|
+
|
|
172
|
+
packed_data << compressed
|
|
173
|
+
pack_sizes << compressed.bytesize
|
|
174
|
+
unpack_sizes << data.bytesize
|
|
175
|
+
|
|
176
|
+
# Calculate CRC
|
|
177
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
178
|
+
crc.update(data)
|
|
179
|
+
entry.crc = crc.value
|
|
180
|
+
digests << crc.value
|
|
181
|
+
|
|
182
|
+
folders << {
|
|
183
|
+
method_id: compressor.method_id,
|
|
184
|
+
properties: compressor.properties,
|
|
185
|
+
unpack_size: data.bytesize,
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
{
|
|
190
|
+
data: packed_data,
|
|
191
|
+
folders: folders,
|
|
192
|
+
pack_sizes: pack_sizes,
|
|
193
|
+
unpack_sizes: unpack_sizes,
|
|
194
|
+
digests: digests,
|
|
195
|
+
}
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Build next header metadata
|
|
200
|
+
#
|
|
201
|
+
# @param compressed_result [Hash] Compression results
|
|
202
|
+
# @return [String] Encoded next header
|
|
203
|
+
def build_next_header(compressed_result)
|
|
204
|
+
header_writer = HeaderWriter.new
|
|
205
|
+
|
|
206
|
+
header_options = {
|
|
207
|
+
streams: {
|
|
208
|
+
pack_pos: 0,
|
|
209
|
+
pack_sizes: compressed_result[:pack_sizes],
|
|
210
|
+
pack_crcs: [],
|
|
211
|
+
folders: compressed_result[:folders],
|
|
212
|
+
unpack_sizes: compressed_result[:unpack_sizes],
|
|
213
|
+
digests: compressed_result[:digests],
|
|
214
|
+
},
|
|
215
|
+
entries: @entries,
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
header_writer.write_next_header(header_options)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Start first volume
|
|
222
|
+
def start_first_volume
|
|
223
|
+
volume_path = @split_options.volume_filename(@base_path, 1)
|
|
224
|
+
@current_volume = File.open(volume_path, "wb")
|
|
225
|
+
@volumes << VolumeInfo.new(volume_path, 0)
|
|
226
|
+
|
|
227
|
+
# Reserve space for start header (will be written at end)
|
|
228
|
+
@current_volume.write("\0" * START_HEADER_SIZE)
|
|
229
|
+
@global_offset = START_HEADER_SIZE
|
|
230
|
+
@volumes.last.size = START_HEADER_SIZE
|
|
231
|
+
@volumes.last.end_offset = START_HEADER_SIZE
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Write packed data across volumes
|
|
235
|
+
#
|
|
236
|
+
# @param data [String] Data to write
|
|
237
|
+
def write_packed_data(data)
|
|
238
|
+
offset = 0
|
|
239
|
+
remaining = data.bytesize
|
|
240
|
+
|
|
241
|
+
while remaining.positive?
|
|
242
|
+
available = @split_options.volume_size - @volumes.last.size
|
|
243
|
+
|
|
244
|
+
# If current volume is full, start a new one
|
|
245
|
+
if available <= 0
|
|
246
|
+
close_current_volume
|
|
247
|
+
start_continuation_volume
|
|
248
|
+
available = @split_options.volume_size
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
chunk_size = [available, remaining].min
|
|
252
|
+
|
|
253
|
+
if chunk_size.positive?
|
|
254
|
+
chunk = data[offset, chunk_size]
|
|
255
|
+
@current_volume.write(chunk)
|
|
256
|
+
@volumes.last.size += chunk_size
|
|
257
|
+
@volumes.last.end_offset += chunk_size
|
|
258
|
+
@global_offset += chunk_size
|
|
259
|
+
offset += chunk_size
|
|
260
|
+
remaining -= chunk_size
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Write data (may span volumes)
|
|
266
|
+
#
|
|
267
|
+
# @param data [String] Data to write
|
|
268
|
+
def write_data(data)
|
|
269
|
+
write_packed_data(data)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Start continuation volume
|
|
273
|
+
def start_continuation_volume
|
|
274
|
+
@current_volume_number += 1
|
|
275
|
+
volume_path = @split_options.volume_filename(@base_path,
|
|
276
|
+
@current_volume_number)
|
|
277
|
+
@current_volume = File.open(volume_path, "wb")
|
|
278
|
+
@volumes << VolumeInfo.new(volume_path, @global_offset)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Write start header to first volume
|
|
282
|
+
#
|
|
283
|
+
# @param header_data [String] Header data
|
|
284
|
+
# @param header_offset [Integer] Offset to header
|
|
285
|
+
def write_start_header_to_first_volume(header_data, header_offset)
|
|
286
|
+
# Close current volume first to ensure all data is flushed
|
|
287
|
+
close_current_volume
|
|
288
|
+
|
|
289
|
+
header_writer = HeaderWriter.new
|
|
290
|
+
start_header = header_writer.write_start_header(
|
|
291
|
+
header_data,
|
|
292
|
+
header_offset,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# Open first volume and write start header
|
|
296
|
+
first_volume_path = @volumes.first.path
|
|
297
|
+
File.open(first_volume_path, "r+b") do |io|
|
|
298
|
+
io.seek(0)
|
|
299
|
+
io.write(start_header)
|
|
300
|
+
io.flush
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Close current volume
|
|
305
|
+
def close_current_volume
|
|
306
|
+
return unless @current_volume
|
|
307
|
+
|
|
308
|
+
@current_volume.flush
|
|
309
|
+
@current_volume.close
|
|
310
|
+
@current_volume = nil
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
require_relative "../../checksums/crc32"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module SevenZip
|
|
9
|
+
# Compresses data streams using coder chains
|
|
10
|
+
# Opposite of StreamDecompressor - applies compression algorithms
|
|
11
|
+
class StreamCompressor
|
|
12
|
+
attr_reader :algorithm, :level, :filters
|
|
13
|
+
|
|
14
|
+
# Initialize compressor
|
|
15
|
+
#
|
|
16
|
+
# @param algorithm [Symbol] Compression algorithm
|
|
17
|
+
# @param level [Integer] Compression level (1-9)
|
|
18
|
+
# @param filters [Array<Symbol>] Filter chain
|
|
19
|
+
def initialize(algorithm: :lzma2, level: 5, filters: [])
|
|
20
|
+
@algorithm = algorithm
|
|
21
|
+
@level = level
|
|
22
|
+
@filters = Array(filters)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Compress data
|
|
26
|
+
#
|
|
27
|
+
# @param data [String] Uncompressed data
|
|
28
|
+
# @return [String] Compressed data
|
|
29
|
+
def compress(data)
|
|
30
|
+
result = data
|
|
31
|
+
|
|
32
|
+
# Apply filters first
|
|
33
|
+
@filters.each do |filter_sym|
|
|
34
|
+
filter_class = FilterRegistry.get(filter_sym)
|
|
35
|
+
next unless filter_class
|
|
36
|
+
|
|
37
|
+
filter = filter_class.new
|
|
38
|
+
input_io = StringIO.new(result)
|
|
39
|
+
output_io = StringIO.new
|
|
40
|
+
output_io.set_encoding("BINARY")
|
|
41
|
+
filter.encode(input_io, output_io)
|
|
42
|
+
result = output_io.string
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Apply compression algorithm
|
|
46
|
+
if @algorithm && @algorithm != :copy
|
|
47
|
+
algo_class = AlgorithmRegistry.get(@algorithm)
|
|
48
|
+
raise "Algorithm not found: #{@algorithm}" unless algo_class
|
|
49
|
+
|
|
50
|
+
encoder = algo_class.new
|
|
51
|
+
input_io = StringIO.new(result)
|
|
52
|
+
output_io = StringIO.new
|
|
53
|
+
output_io.set_encoding("BINARY")
|
|
54
|
+
|
|
55
|
+
# For 7-Zip format, use raw_mode (no property byte in compressed data)
|
|
56
|
+
# The properties are encoded in the 7-Zip header instead
|
|
57
|
+
encoder.compress(input_io, output_io, { raw_mode: true, standalone: false })
|
|
58
|
+
result = output_io.string
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
result
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Compress multiple files into single stream (solid compression)
|
|
65
|
+
#
|
|
66
|
+
# @param file_entries [Array<Models::FileEntry>] Files to compress
|
|
67
|
+
# @return [Hash] Compression result with packed/unpacked sizes
|
|
68
|
+
def compress_files(file_entries)
|
|
69
|
+
# Concatenate all file data
|
|
70
|
+
combined_data = String.new(encoding: "BINARY")
|
|
71
|
+
unpack_sizes = []
|
|
72
|
+
crcs = []
|
|
73
|
+
|
|
74
|
+
file_entries.each do |entry|
|
|
75
|
+
next unless entry.has_stream? && entry.source_path
|
|
76
|
+
|
|
77
|
+
data = File.binread(entry.source_path)
|
|
78
|
+
combined_data << data
|
|
79
|
+
unpack_sizes << data.bytesize
|
|
80
|
+
|
|
81
|
+
# Calculate CRC
|
|
82
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
83
|
+
crc.update(data)
|
|
84
|
+
crcs << crc.value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Compress combined data
|
|
88
|
+
packed_data = compress(combined_data)
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
packed_data: packed_data,
|
|
92
|
+
packed_size: packed_data.bytesize,
|
|
93
|
+
unpack_size: combined_data.bytesize,
|
|
94
|
+
unpack_sizes: unpack_sizes,
|
|
95
|
+
crcs: crcs,
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Get method ID for this compression algorithm
|
|
100
|
+
#
|
|
101
|
+
# @return [Integer] Method ID
|
|
102
|
+
def method_id
|
|
103
|
+
case @algorithm
|
|
104
|
+
when :copy then Constants::MethodId::COPY
|
|
105
|
+
when :lzma then Constants::MethodId::LZMA
|
|
106
|
+
when :lzma2 then Constants::MethodId::LZMA2
|
|
107
|
+
when :ppmd, :ppmd7 then Constants::MethodId::PPMD
|
|
108
|
+
when :bzip2 then Constants::MethodId::BZIP2
|
|
109
|
+
else Constants::MethodId::LZMA2
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Get filter IDs for filter chain
|
|
114
|
+
#
|
|
115
|
+
# @return [Array<Integer>] Filter IDs
|
|
116
|
+
def filter_ids
|
|
117
|
+
@filters.filter_map do |filter_sym|
|
|
118
|
+
case filter_sym
|
|
119
|
+
when :bcj_x86 then Constants::FilterId::BCJ_X86
|
|
120
|
+
when :delta then Constants::FilterId::DELTA
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Get properties for compression algorithm
|
|
126
|
+
#
|
|
127
|
+
# @return [String, nil] Binary properties
|
|
128
|
+
def properties
|
|
129
|
+
return nil if @algorithm == :copy || @algorithm.nil?
|
|
130
|
+
|
|
131
|
+
case @algorithm
|
|
132
|
+
when :lzma2
|
|
133
|
+
# LZMA2 properties: dictionary size encoded
|
|
134
|
+
dict_size = 1 << (15 + @level)
|
|
135
|
+
prop = 0
|
|
136
|
+
prop += 1 while dict_size > (2 << prop)
|
|
137
|
+
[prop].pack("C")
|
|
138
|
+
when :lzma
|
|
139
|
+
# LZMA properties: lc, lp, pb, dict_size
|
|
140
|
+
lc = 3
|
|
141
|
+
lp = 0
|
|
142
|
+
pb = 2
|
|
143
|
+
dict_size = 1 << (15 + @level)
|
|
144
|
+
[lc + (lp * 9) + (pb * 9 * 5)].pack("C") +
|
|
145
|
+
[dict_size].pack("V")
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "coder_chain"
|
|
4
|
+
require_relative "../../checksums/crc32"
|
|
5
|
+
require "stringio"
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Formats
|
|
9
|
+
module SevenZip
|
|
10
|
+
# Decompresses .7z streams using coder chains
|
|
11
|
+
# Handles packed stream extraction and CRC validation
|
|
12
|
+
class StreamDecompressor
|
|
13
|
+
attr_reader :archive_io, :folder, :chain_config
|
|
14
|
+
|
|
15
|
+
# Initialize decompressor
|
|
16
|
+
#
|
|
17
|
+
# @param archive_io [IO] Archive file handle
|
|
18
|
+
# @param folder [Models::Folder] Folder specification
|
|
19
|
+
# @param pack_pos [Integer] Position of packed data
|
|
20
|
+
# @param pack_size [Integer] Size of packed data
|
|
21
|
+
# @param header [Header, nil] Optional header for split archive handling
|
|
22
|
+
def initialize(archive_io, folder, pack_pos, pack_size, header = nil)
|
|
23
|
+
@archive_io = archive_io
|
|
24
|
+
@folder = folder
|
|
25
|
+
@pack_pos = pack_pos
|
|
26
|
+
@pack_size = pack_size
|
|
27
|
+
@header = header
|
|
28
|
+
@chain_config = CoderChain.build_from_folder(folder)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Decompress stream to output
|
|
32
|
+
#
|
|
33
|
+
# @param size [Integer] Expected uncompressed size
|
|
34
|
+
# @return [String] Decompressed data
|
|
35
|
+
# @raise [RuntimeError] on decompression error
|
|
36
|
+
def decompress(size)
|
|
37
|
+
# Seek to packed data
|
|
38
|
+
@archive_io.seek(@pack_pos)
|
|
39
|
+
|
|
40
|
+
# For multi-volume archives, pack_size from stream_info may be incomplete.
|
|
41
|
+
# For solid archives spanning volumes, the actual compressed data extends
|
|
42
|
+
# from pack_pos to where the next header starts.
|
|
43
|
+
if @archive_io.is_a?(SplitArchiveReader::MultiVolumeIO) && @header
|
|
44
|
+
# next_header_offset is relative to start_pos_after_header
|
|
45
|
+
# Actual packed data size is from pack_pos to next header position
|
|
46
|
+
next_header_position = @header.start_pos_after_header + @header.next_header_offset
|
|
47
|
+
actual_pack_size = next_header_position - @pack_pos
|
|
48
|
+
packed_data = @archive_io.read(actual_pack_size)
|
|
49
|
+
else
|
|
50
|
+
# Regular single-file archive: use pack_size from stream info
|
|
51
|
+
packed_data = @archive_io.read(@pack_size)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
return packed_data if @chain_config.nil? # No compression
|
|
55
|
+
|
|
56
|
+
# Get algorithm
|
|
57
|
+
algo_sym = @chain_config[:algorithm]
|
|
58
|
+
return packed_data unless algo_sym # Copy method
|
|
59
|
+
|
|
60
|
+
# Get algorithm class
|
|
61
|
+
algo_class = AlgorithmRegistry.get(algo_sym)
|
|
62
|
+
raise "Algorithm not found: #{algo_sym}" unless algo_class
|
|
63
|
+
|
|
64
|
+
# Decompress
|
|
65
|
+
input_io = StringIO.new(packed_data)
|
|
66
|
+
output_io = StringIO.new
|
|
67
|
+
output_io.set_encoding("BINARY")
|
|
68
|
+
|
|
69
|
+
# Build options from coder properties for 7-Zip format
|
|
70
|
+
decoder_options = build_decoder_options
|
|
71
|
+
|
|
72
|
+
decoder = algo_class.new(decoder_options)
|
|
73
|
+
decoder.decompress(input_io, output_io, size: size)
|
|
74
|
+
|
|
75
|
+
result = output_io.string
|
|
76
|
+
|
|
77
|
+
# Apply filters if present
|
|
78
|
+
if @chain_config[:filters] && !@chain_config[:filters].empty?
|
|
79
|
+
@chain_config[:filters].reverse_each do |filter_sym|
|
|
80
|
+
filter_class = FilterRegistry.get(filter_sym)
|
|
81
|
+
next unless filter_class
|
|
82
|
+
|
|
83
|
+
filter = filter_class.new
|
|
84
|
+
filtered = StringIO.new
|
|
85
|
+
filter.reverse(StringIO.new(result), filtered)
|
|
86
|
+
result = filtered.string
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
result
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Decompress and verify CRC
|
|
94
|
+
#
|
|
95
|
+
# @param size [Integer] Expected uncompressed size
|
|
96
|
+
# @param expected_crc [Integer, nil] Expected CRC32 value
|
|
97
|
+
# @return [String] Decompressed data
|
|
98
|
+
# @raise [RuntimeError] if CRC mismatch
|
|
99
|
+
def decompress_and_verify(size, expected_crc = nil)
|
|
100
|
+
data = decompress(size)
|
|
101
|
+
|
|
102
|
+
if expected_crc
|
|
103
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
104
|
+
crc.update(data)
|
|
105
|
+
actual_crc = crc.value
|
|
106
|
+
|
|
107
|
+
unless actual_crc == expected_crc
|
|
108
|
+
raise "CRC mismatch: expected 0x#{expected_crc.to_s(16)}, " \
|
|
109
|
+
"got 0x#{actual_crc.to_s(16)}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
data
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
# Build decoder options from coder properties
|
|
119
|
+
#
|
|
120
|
+
# For 7-Zip format, coder properties contain algorithm-specific data.
|
|
121
|
+
# For LZMA2, it's a single byte encoding the dictionary size.
|
|
122
|
+
#
|
|
123
|
+
# @return [Hash] Decoder options
|
|
124
|
+
def build_decoder_options
|
|
125
|
+
return {} unless @chain_config
|
|
126
|
+
|
|
127
|
+
options = {}
|
|
128
|
+
properties = @chain_config[:properties]
|
|
129
|
+
|
|
130
|
+
if properties && !properties.empty?
|
|
131
|
+
algo_sym = @chain_config[:algorithm]
|
|
132
|
+
|
|
133
|
+
case algo_sym
|
|
134
|
+
when :lzma2
|
|
135
|
+
# LZMA2 properties: single byte encoding dictionary size
|
|
136
|
+
prop_byte = properties.getbyte(0)
|
|
137
|
+
dict_size = Omnizip::Algorithms::LZMA2::Properties.decode_dict_size(prop_byte)
|
|
138
|
+
options[:raw_mode] = true # No property byte in data stream
|
|
139
|
+
options[:dict_size] = dict_size
|
|
140
|
+
when :lzma
|
|
141
|
+
# LZMA properties: 5 bytes (prop byte + dict size)
|
|
142
|
+
# Format: 1 byte (lc/lp/pb) + 4 bytes (dict size LE)
|
|
143
|
+
if properties.bytesize >= 5
|
|
144
|
+
props_byte = properties.getbyte(0)
|
|
145
|
+
dict_size = properties[1, 4].unpack1("V")
|
|
146
|
+
# Use lzma2_mode to skip header reading - 7-Zip provides properties separately
|
|
147
|
+
options[:lzma2_mode] = true
|
|
148
|
+
options[:lc] = props_byte % 9
|
|
149
|
+
remainder = props_byte / 9
|
|
150
|
+
options[:lp] = remainder % 5
|
|
151
|
+
options[:pb] = remainder / 5
|
|
152
|
+
options[:dict_size] = dict_size
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
options
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|