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,175 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
require_relative "../algorithms/lzma"
|
|
5
|
+
require_relative "../format_registry"
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Formats
|
|
9
|
+
#
|
|
10
|
+
# LZIP (.lz) compression format
|
|
11
|
+
#
|
|
12
|
+
# Lzip is a file compression format that uses LZMA compression with a
|
|
13
|
+
# simple container format. It was created as an alternative to the
|
|
14
|
+
# legacy .lzma format with better integrity checking.
|
|
15
|
+
#
|
|
16
|
+
# This is DIFFERENT from both XZ and .lzma (LZMA_Alone) formats:
|
|
17
|
+
# - XZ (.xz): Container format with stream header/footer/index, LZMA2 compression
|
|
18
|
+
# - LZIP (.lz): Standalone format with "LZIP" magic and CRC32 footer, LZMA1 compression
|
|
19
|
+
# - LZMA_Alone (.lzma): Legacy standalone format with properties byte, LZMA1 compression
|
|
20
|
+
#
|
|
21
|
+
# Format structure:
|
|
22
|
+
# - Header (6 bytes):
|
|
23
|
+
# - Magic bytes: "LZIP" (0x4C 0x5A 0x49 0x50)
|
|
24
|
+
# - Version (1 byte): 0 or 1
|
|
25
|
+
# - Dictionary size (1 byte): encoded format
|
|
26
|
+
# - LZMA1 compressed stream (with fixed LC=3, LP=0, PB=2)
|
|
27
|
+
# - Footer:
|
|
28
|
+
# - Version 0 (12 bytes): CRC32 (4) + Uncompressed size (8)
|
|
29
|
+
# - Version 1 (20 bytes): CRC32 (4) + Uncompressed size (8) + Member size (8)
|
|
30
|
+
#
|
|
31
|
+
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/lzip_decoder.c
|
|
32
|
+
#
|
|
33
|
+
module Lzip
|
|
34
|
+
# Lzip magic bytes: "LZIP" in ASCII
|
|
35
|
+
# Reference: lzip_decoder.c:106
|
|
36
|
+
MAGIC = [0x4C, 0x5A, 0x49, 0x50].pack("C*")
|
|
37
|
+
|
|
38
|
+
# Fixed LC/LP/PB values for lzip format
|
|
39
|
+
# Reference: lzip_decoder.c:23-26
|
|
40
|
+
LZIP_LC = 3
|
|
41
|
+
LZIP_LP = 0
|
|
42
|
+
LZIP_PB = 2
|
|
43
|
+
|
|
44
|
+
# Minimum and maximum dictionary sizes (in bytes)
|
|
45
|
+
# Reference: lzip_decoder.c:197-198
|
|
46
|
+
MIN_DICT_SIZE = 4096 # 4 KiB
|
|
47
|
+
MAX_DICT_SIZE = (512 << 20) # 512 MiB
|
|
48
|
+
|
|
49
|
+
class << self
|
|
50
|
+
# Compress a file with LZIP
|
|
51
|
+
#
|
|
52
|
+
# @param input_path [String] Input file path
|
|
53
|
+
# @param output_path [String] Output LZIP file path
|
|
54
|
+
# @param options [Hash] Compression options
|
|
55
|
+
# @option options [Integer] :level Compression level (0-9, default: 6)
|
|
56
|
+
# @option options [Integer] :dict_size Dictionary size (default: auto from level)
|
|
57
|
+
# @option options [Integer] :version LZIP version (0 or 1, default: 1)
|
|
58
|
+
def compress(input_path, output_path, options = {})
|
|
59
|
+
input_data = File.binread(input_path)
|
|
60
|
+
|
|
61
|
+
File.open(output_path, "wb") do |output|
|
|
62
|
+
compress_stream(
|
|
63
|
+
StringIO.new(input_data),
|
|
64
|
+
output,
|
|
65
|
+
options,
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Decompress LZIP data
|
|
71
|
+
#
|
|
72
|
+
# @param input [String, IO] Input data, file path, or IO object
|
|
73
|
+
# @param output [String, IO, nil] Output file path or IO object
|
|
74
|
+
# @param options [Hash] Options
|
|
75
|
+
# @option options [Boolean] :ignore_check If true, skip CRC32 verification (default: false)
|
|
76
|
+
# @option options [Boolean] :concatenated If true, decode concatenated .lz members (default: false)
|
|
77
|
+
# @return [String, nil] Decompressed data (if output is nil)
|
|
78
|
+
def decompress(input, output = nil, options = {})
|
|
79
|
+
# Handle raw data string vs file path vs IO object
|
|
80
|
+
data = if input.respond_to?(:read)
|
|
81
|
+
# Already an IO object
|
|
82
|
+
input.read
|
|
83
|
+
elsif input.is_a?(String)
|
|
84
|
+
# Could be file path or raw data
|
|
85
|
+
if !input.include?("\0") && File.exist?(input)
|
|
86
|
+
File.binread(input)
|
|
87
|
+
else
|
|
88
|
+
input.b
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
raise ArgumentError,
|
|
92
|
+
"Input must be a String or IO object"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Decode using LzipDecoder
|
|
96
|
+
require_relative "../algorithms/lzma/lzip_decoder"
|
|
97
|
+
decoder = Omnizip::Algorithms::LZMA::LzipDecoder.new(
|
|
98
|
+
StringIO.new(data),
|
|
99
|
+
options,
|
|
100
|
+
)
|
|
101
|
+
result = decoder.decode_stream
|
|
102
|
+
|
|
103
|
+
if output
|
|
104
|
+
if output.respond_to?(:write)
|
|
105
|
+
output.write(result)
|
|
106
|
+
else
|
|
107
|
+
File.binwrite(output, result)
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
result
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Decode LZIP data (alias for decompress with no output)
|
|
115
|
+
#
|
|
116
|
+
# @param input [String, IO] Input LZIP data or file path
|
|
117
|
+
# @return [String] Decompressed data
|
|
118
|
+
def decode(input)
|
|
119
|
+
decompress(input)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Compress data stream with LZIP
|
|
123
|
+
#
|
|
124
|
+
# @param input_io [IO] Input stream
|
|
125
|
+
# @param output_io [IO] Output stream
|
|
126
|
+
# @param options [Hash] Compression options
|
|
127
|
+
def compress_stream(input_io, _output_io, _options = {})
|
|
128
|
+
input_io.read
|
|
129
|
+
|
|
130
|
+
# TODO: Implement LZIP encoder
|
|
131
|
+
raise NotImplementedError,
|
|
132
|
+
"LZIP encoding not yet implemented. Use decompression only."
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Decompress LZIP stream
|
|
136
|
+
#
|
|
137
|
+
# @param input_io [IO] Input stream
|
|
138
|
+
# @param output_io [IO] Output stream
|
|
139
|
+
# @param options [Hash] Options
|
|
140
|
+
# @return [Hash] Metadata (version, dict_size, member_size)
|
|
141
|
+
def decompress_stream(input_io, output_io, options = {})
|
|
142
|
+
require_relative "../algorithms/lzma/lzip_decoder"
|
|
143
|
+
decoder = Omnizip::Algorithms::LZMA::LzipDecoder.new(input_io, options)
|
|
144
|
+
result = decoder.decode_stream
|
|
145
|
+
|
|
146
|
+
output_io.write(result)
|
|
147
|
+
|
|
148
|
+
# Return metadata
|
|
149
|
+
{
|
|
150
|
+
version: decoder.instance_variable_get(:@version),
|
|
151
|
+
dict_size: decoder.instance_variable_get(:@dict_size),
|
|
152
|
+
member_size: decoder.instance_variable_get(:@member_size),
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Register LZIP format when loaded
|
|
157
|
+
def register!
|
|
158
|
+
FormatRegistry.register(".lz", Omnizip::Formats::Lzip)
|
|
159
|
+
FormatRegistry.register(".lzip", Omnizip::Formats::Lzip)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Detect if data is LZIP format
|
|
163
|
+
#
|
|
164
|
+
# @param data [String] Data to check
|
|
165
|
+
# @return [Boolean] true if data has LZIP magic bytes
|
|
166
|
+
def lzip_file?(data)
|
|
167
|
+
data.start_with?(MAGIC)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Auto-register on load
|
|
175
|
+
Omnizip::Formats::Lzip.register!
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
require_relative "../algorithms/lzma"
|
|
5
|
+
require_relative "../format_registry"
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Formats
|
|
9
|
+
#
|
|
10
|
+
# LZMA_Alone (.lzma) compression format
|
|
11
|
+
#
|
|
12
|
+
# This is the legacy LZMA_Alone format used by LZMA Utils 4.32.x.
|
|
13
|
+
# It is DIFFERENT from both XZ and LZIP formats:
|
|
14
|
+
# - XZ (.xz): Container format with stream header/footer/index, LZMA2 compression
|
|
15
|
+
# - LZIP (.lz): Standalone format with "LZIP" magic and CRC32 footer, LZMA1 compression
|
|
16
|
+
# - LZMA_Alone (.lzma): Legacy standalone format with properties byte, LZMA1 compression
|
|
17
|
+
#
|
|
18
|
+
# Format structure:
|
|
19
|
+
# - Properties (1 byte): encodes lc, lp, pb values
|
|
20
|
+
# - Dictionary size (4 bytes, little-endian)
|
|
21
|
+
# - Uncompressed size (8 bytes, little-endian, UINT64_MAX = unknown)
|
|
22
|
+
# - LZMA1 compressed stream (no footer, no CRC32)
|
|
23
|
+
#
|
|
24
|
+
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/alone_decoder.c
|
|
25
|
+
#
|
|
26
|
+
module LzmaAlone
|
|
27
|
+
# Maximum valid uncompressed size (256 GiB)
|
|
28
|
+
# From alone_decoder.c:118
|
|
29
|
+
MAX_UNCOMPRESSED_SIZE = (1 << 38)
|
|
30
|
+
|
|
31
|
+
# Property byte validation limits
|
|
32
|
+
# From lzma_decoder.c:1218
|
|
33
|
+
MAX_PROPERTY_BYTE = (((4 * 5) + 4) * 9) + 8 # = 233
|
|
34
|
+
|
|
35
|
+
# Value indicating unknown uncompressed size
|
|
36
|
+
UINT64_MAX = (1 << 64) - 1
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
# Compress a file with LZMA_Alone format
|
|
40
|
+
#
|
|
41
|
+
# @param input_path [String] Input file path
|
|
42
|
+
# @param output_path [String] Output LZMA_Alone file path
|
|
43
|
+
# @param options [Hash] Compression options
|
|
44
|
+
# @option options [Integer] :lc Literal context bits (0-8, default: 3)
|
|
45
|
+
# @option options [Integer] :lp Literal position bits (0-4, default: 0)
|
|
46
|
+
# @option options [Integer] :pb Position bits (0-4, default: 2)
|
|
47
|
+
# @option options [Integer] :dict_size Dictionary size (default: 8MB)
|
|
48
|
+
# @option options [Integer] :uncompressed_size Explicit uncompressed size (default: unknown)
|
|
49
|
+
def compress(input_path, output_path, options = {})
|
|
50
|
+
input_data = File.binread(input_path)
|
|
51
|
+
|
|
52
|
+
File.open(output_path, "wb") do |output|
|
|
53
|
+
compress_stream(
|
|
54
|
+
StringIO.new(input_data),
|
|
55
|
+
output,
|
|
56
|
+
options,
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Decompress LZMA_Alone data
|
|
62
|
+
#
|
|
63
|
+
# @param input [String, IO] Input data, file path, or IO object
|
|
64
|
+
# @param output [String, IO, nil] Output file path or IO object
|
|
65
|
+
# @param options [Hash] Options
|
|
66
|
+
# @option options [Boolean] :picky If true, reject files unlikely to be .lzma (default: false)
|
|
67
|
+
# @return [String, nil] Decompressed data (if output is nil)
|
|
68
|
+
def decompress(input, output = nil, options = {})
|
|
69
|
+
# Handle raw data string vs file path
|
|
70
|
+
data = if input.respond_to?(:read)
|
|
71
|
+
input.read
|
|
72
|
+
elsif input.is_a?(String)
|
|
73
|
+
if !input.include?("\0") && File.exist?(input)
|
|
74
|
+
File.binread(input)
|
|
75
|
+
else
|
|
76
|
+
input.b
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
raise ArgumentError,
|
|
80
|
+
"Input must be a String or IO object"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Decode using LzmaAloneDecoder
|
|
84
|
+
require_relative "../algorithms/lzma/lzma_alone_decoder"
|
|
85
|
+
decoder = Omnizip::Algorithms::LZMA::LzmaAloneDecoder.new(
|
|
86
|
+
StringIO.new(data),
|
|
87
|
+
options,
|
|
88
|
+
)
|
|
89
|
+
result = decoder.decode_stream
|
|
90
|
+
|
|
91
|
+
if output
|
|
92
|
+
if output.respond_to?(:write)
|
|
93
|
+
output.write(result)
|
|
94
|
+
else
|
|
95
|
+
File.binwrite(output, result)
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
result
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Decode LZMA_Alone data (alias for decompress with no output)
|
|
103
|
+
#
|
|
104
|
+
# @param input [String, IO] Input LZMA_Alone data or file path
|
|
105
|
+
# @return [String] Decompressed data
|
|
106
|
+
def decode(input)
|
|
107
|
+
decompress(input)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Compress data stream with LZMA_Alone format
|
|
111
|
+
#
|
|
112
|
+
# @param input_io [IO] Input stream
|
|
113
|
+
# @param output_io [IO] Output stream
|
|
114
|
+
# @param options [Hash] Compression options
|
|
115
|
+
def compress_stream(input_io, output_io, options = {})
|
|
116
|
+
input_data = input_io.read
|
|
117
|
+
|
|
118
|
+
# Get encoding parameters
|
|
119
|
+
lc = options[:lc] || 3
|
|
120
|
+
lp = options[:lp] || 0
|
|
121
|
+
pb = options[:pb] || 2
|
|
122
|
+
dict_size = options[:dict_size] || (8 * 1024 * 1024)
|
|
123
|
+
uncompressed_size = options[:uncompressed_size] || input_data.bytesize
|
|
124
|
+
|
|
125
|
+
# Encode properties byte
|
|
126
|
+
prop_byte = (((pb * 5) + lp) * 9) + lc
|
|
127
|
+
|
|
128
|
+
# Write header
|
|
129
|
+
output_io.write([prop_byte].pack("C"))
|
|
130
|
+
output_io.write([dict_size].pack("V"))
|
|
131
|
+
output_io.write([uncompressed_size].pack("Q<"))
|
|
132
|
+
|
|
133
|
+
# TODO: Implement LZMA1 encoder
|
|
134
|
+
raise NotImplementedError,
|
|
135
|
+
"LZMA_Alone encoding not yet implemented. Use decompression only."
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Decompress LZMA_Alone stream
|
|
139
|
+
#
|
|
140
|
+
# @param input_io [IO] Input stream
|
|
141
|
+
# @param output_io [IO] Output stream
|
|
142
|
+
# @param options [Hash] Options
|
|
143
|
+
# @return [Hash] Metadata (lc, lp, pb, dict_size, uncompressed_size)
|
|
144
|
+
def decompress_stream(input_io, output_io, options = {})
|
|
145
|
+
require_relative "../algorithms/lzma/lzma_alone_decoder"
|
|
146
|
+
decoder = Omnizip::Algorithms::LZMA::LzmaAloneDecoder.new(input_io, options)
|
|
147
|
+
result = decoder.decode_stream
|
|
148
|
+
|
|
149
|
+
output_io.write(result)
|
|
150
|
+
|
|
151
|
+
# Return metadata
|
|
152
|
+
{
|
|
153
|
+
lc: decoder.instance_variable_get(:@lc),
|
|
154
|
+
lp: decoder.instance_variable_get(:@lp),
|
|
155
|
+
pb: decoder.instance_variable_get(:@pb),
|
|
156
|
+
dict_size: decoder.instance_variable_get(:@dict_size),
|
|
157
|
+
uncompressed_size: decoder.instance_variable_get(:@uncompressed_size),
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Register LZMA_Alone format when loaded
|
|
162
|
+
def register!
|
|
163
|
+
FormatRegistry.register(".lzma", Omnizip::Formats::LzmaAlone)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Auto-register on load
|
|
171
|
+
Omnizip::Formats::LzmaAlone.register!
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "archive_verifier"
|
|
4
|
+
require_relative "recovery_record"
|
|
5
|
+
require_relative "parity_handler"
|
|
6
|
+
require "fileutils"
|
|
7
|
+
|
|
8
|
+
module Omnizip
|
|
9
|
+
module Formats
|
|
10
|
+
module Rar
|
|
11
|
+
# RAR archive repair functionality
|
|
12
|
+
# Attempts to repair corrupted archives using recovery records
|
|
13
|
+
class ArchiveRepairer
|
|
14
|
+
attr_reader :verifier, :recovery_record, :parity_handler
|
|
15
|
+
|
|
16
|
+
# Repair result
|
|
17
|
+
class RepairResult
|
|
18
|
+
attr_accessor :success, :repaired_files, :unrepaired_files,
|
|
19
|
+
:repaired_blocks, :errors, :output_path
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
@success = false
|
|
23
|
+
@repaired_files = []
|
|
24
|
+
@unrepaired_files = []
|
|
25
|
+
@repaired_blocks = []
|
|
26
|
+
@errors = []
|
|
27
|
+
@output_path = nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Check if repair was successful
|
|
31
|
+
#
|
|
32
|
+
# @return [Boolean] true if all files repaired
|
|
33
|
+
def success?
|
|
34
|
+
@success && @unrepaired_files.empty?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Get repair summary
|
|
38
|
+
#
|
|
39
|
+
# @return [String] Repair summary
|
|
40
|
+
def summary
|
|
41
|
+
if success?
|
|
42
|
+
"Repair successful: #{@repaired_files.size} files, " \
|
|
43
|
+
"#{@repaired_blocks.size} blocks"
|
|
44
|
+
elsif @repaired_files.any?
|
|
45
|
+
"Partial repair: #{@repaired_files.size} files OK, " \
|
|
46
|
+
"#{@unrepaired_files.size} failed"
|
|
47
|
+
else
|
|
48
|
+
"Repair failed: #{@errors.join(', ')}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Initialize repairer
|
|
54
|
+
def initialize
|
|
55
|
+
@verifier = nil
|
|
56
|
+
@recovery_record = nil
|
|
57
|
+
@parity_handler = nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Repair corrupted archive
|
|
61
|
+
#
|
|
62
|
+
# @param input_path [String] Path to corrupted archive
|
|
63
|
+
# @param output_path [String] Path for repaired archive
|
|
64
|
+
# @param options [Hash] Repair options
|
|
65
|
+
# @option options [Boolean] :use_external_rev Use external .rev files
|
|
66
|
+
# @option options [Boolean] :verify_repaired Verify after repair
|
|
67
|
+
# @option options [Boolean] :verbose Enable verbose output
|
|
68
|
+
# @return [RepairResult] Repair results
|
|
69
|
+
def repair(input_path, output_path, options = {})
|
|
70
|
+
result = RepairResult.new
|
|
71
|
+
result.output_path = output_path
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
# Verify archive first
|
|
75
|
+
@verifier = ArchiveVerifier.new(input_path)
|
|
76
|
+
verification = @verifier.verify(
|
|
77
|
+
use_recovery: true,
|
|
78
|
+
verbose: options[:verbose],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
unless verification.recovery_available
|
|
82
|
+
result.errors << "No recovery records available"
|
|
83
|
+
return result
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
unless verification.can_repair?
|
|
87
|
+
result.errors << "Corruption not recoverable"
|
|
88
|
+
return result
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Perform repair
|
|
92
|
+
@recovery_record = @verifier.recovery_record
|
|
93
|
+
@parity_handler = @verifier.parity_handler
|
|
94
|
+
|
|
95
|
+
perform_repair(input_path, output_path, verification, result,
|
|
96
|
+
options)
|
|
97
|
+
|
|
98
|
+
# Verify repaired archive
|
|
99
|
+
if options[:verify_repaired] && result.success
|
|
100
|
+
verify_repaired_archive(output_path, result)
|
|
101
|
+
end
|
|
102
|
+
rescue StandardError => e
|
|
103
|
+
result.success = false
|
|
104
|
+
result.errors << "Repair error: #{e.message}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
result
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Attempt quick repair (in-place)
|
|
111
|
+
#
|
|
112
|
+
# @param archive_path [String] Path to archive
|
|
113
|
+
# @param options [Hash] Repair options
|
|
114
|
+
# @return [RepairResult] Repair results
|
|
115
|
+
def quick_repair(archive_path, options = {})
|
|
116
|
+
# Create temporary output
|
|
117
|
+
temp_output = "#{archive_path}.repaired"
|
|
118
|
+
|
|
119
|
+
result = repair(archive_path, temp_output, options)
|
|
120
|
+
|
|
121
|
+
if result.success?
|
|
122
|
+
# Replace original with repaired
|
|
123
|
+
FileUtils.mv(temp_output, archive_path, force: true)
|
|
124
|
+
result.output_path = archive_path
|
|
125
|
+
else
|
|
126
|
+
# Clean up temp file
|
|
127
|
+
FileUtils.rm_f(temp_output)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
result
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
# Perform the actual repair
|
|
136
|
+
#
|
|
137
|
+
# @param input_path [String] Input archive path
|
|
138
|
+
# @param output_path [String] Output archive path
|
|
139
|
+
# @param verification [VerificationResult] Verification results
|
|
140
|
+
# @param result [RepairResult] Repair result to update
|
|
141
|
+
# @param options [Hash] Repair options
|
|
142
|
+
def perform_repair(input_path, output_path, verification, result,
|
|
143
|
+
options)
|
|
144
|
+
# Copy archive to output first
|
|
145
|
+
FileUtils.cp(input_path, output_path)
|
|
146
|
+
|
|
147
|
+
# Repair corrupted blocks
|
|
148
|
+
verification.corrupt_blocks.each do |block_index|
|
|
149
|
+
if repair_block(output_path, block_index, options)
|
|
150
|
+
result.repaired_blocks << block_index
|
|
151
|
+
else
|
|
152
|
+
result.errors << "Failed to repair block #{block_index}"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Track repaired files
|
|
157
|
+
verification.corrupted_files.each do |filename|
|
|
158
|
+
if verify_file_in_archive(output_path, filename)
|
|
159
|
+
result.repaired_files << filename
|
|
160
|
+
else
|
|
161
|
+
result.unrepaired_files << filename
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
result.success = result.unrepaired_files.empty?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Repair single block in archive
|
|
169
|
+
#
|
|
170
|
+
# @param archive_path [String] Path to archive
|
|
171
|
+
# @param block_index [Integer] Block to repair
|
|
172
|
+
# @param options [Hash] Repair options
|
|
173
|
+
# @return [Boolean] true if repaired
|
|
174
|
+
def repair_block(archive_path, block_index, options)
|
|
175
|
+
return false unless @parity_handler.can_recover?(block_index)
|
|
176
|
+
|
|
177
|
+
# Recover the block
|
|
178
|
+
recovered_data = @parity_handler.recover_block(archive_path,
|
|
179
|
+
block_index)
|
|
180
|
+
return false unless recovered_data
|
|
181
|
+
|
|
182
|
+
# Write recovered data to archive
|
|
183
|
+
write_block_to_archive(archive_path, block_index, recovered_data)
|
|
184
|
+
|
|
185
|
+
puts "Repaired block #{block_index}" if options[:verbose]
|
|
186
|
+
true
|
|
187
|
+
rescue StandardError => e
|
|
188
|
+
if options[:verbose]
|
|
189
|
+
puts "Failed to repair block #{block_index}: #{e.message}"
|
|
190
|
+
end
|
|
191
|
+
false
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Write block data to archive
|
|
195
|
+
#
|
|
196
|
+
# @param archive_path [String] Path to archive
|
|
197
|
+
# @param block_index [Integer] Block index
|
|
198
|
+
# @param data [String] Block data
|
|
199
|
+
def write_block_to_archive(archive_path, block_index, data)
|
|
200
|
+
block_size = @recovery_record.block_size
|
|
201
|
+
offset = block_index * block_size
|
|
202
|
+
|
|
203
|
+
File.open(archive_path, "r+b") do |io|
|
|
204
|
+
io.seek(offset)
|
|
205
|
+
io.write(data)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Verify file in archive
|
|
210
|
+
#
|
|
211
|
+
# @param archive_path [String] Path to archive
|
|
212
|
+
# @param filename [String] File name to verify
|
|
213
|
+
# @return [Boolean] true if file is valid
|
|
214
|
+
def verify_file_in_archive(archive_path, filename)
|
|
215
|
+
reader = Reader.new(archive_path)
|
|
216
|
+
reader.open
|
|
217
|
+
|
|
218
|
+
entry = reader.entries.find { |e| e.name == filename }
|
|
219
|
+
return false unless entry
|
|
220
|
+
|
|
221
|
+
# Verify entry (simplified - would check CRC in full implementation)
|
|
222
|
+
true
|
|
223
|
+
rescue StandardError
|
|
224
|
+
false
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Verify repaired archive
|
|
228
|
+
#
|
|
229
|
+
# @param archive_path [String] Path to repaired archive
|
|
230
|
+
# @param result [RepairResult] Repair result to update
|
|
231
|
+
def verify_repaired_archive(archive_path, result)
|
|
232
|
+
verifier = ArchiveVerifier.new(archive_path)
|
|
233
|
+
verification = verifier.verify(use_recovery: false)
|
|
234
|
+
|
|
235
|
+
return if verification.valid?
|
|
236
|
+
|
|
237
|
+
result.success = false
|
|
238
|
+
result.errors << "Repaired archive still has errors"
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|