omnizip 0.4.7-arm-linux
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 +293 -0
- data/CHANGELOG.md +1647 -0
- data/CONTEXT.md +68 -0
- data/COPYING +502 -0
- data/Gemfile +19 -0
- data/LICENSE +12 -0
- data/README.adoc +1327 -0
- data/Rakefile +15 -0
- data/TODO.refactor/00-overview.md +67 -0
- data/TODO.refactor/01-registry-base-class.md +81 -0
- data/TODO.refactor/02-error-hierarchy.md +57 -0
- data/TODO.refactor/03-autoload-entry-point.md +69 -0
- data/TODO.refactor/04-filter-base-consolidation.md +49 -0
- data/TODO.refactor/05-send-private-methods.md +64 -0
- data/TODO.refactor/06-instance-variable-access.md +69 -0
- data/TODO.refactor/07-respond-to-replacement.md +237 -0
- data/TODO.refactor/08-spec-doubles.md +27 -0
- data/TODO.refactor/09-cli-shared-module.md +35 -0
- data/TODO.refactor/10-format-detector-ocp.md +34 -0
- data/TODO.refactor/11-convenience-decoupling.md +38 -0
- data/TODO.refactor/12-thread-safety.md +8 -0
- data/TODO.refactor/13-lutaml-model-migration.md +158 -0
- data/TODO.refactor/14-add-missing-specs.md +25 -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 +92 -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 +263 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +183 -0
- data/docs/getting-started/your-first-archive.adoc +219 -0
- data/docs/guides/advanced-features/encryption.adoc +277 -0
- data/docs/guides/advanced-features/index.adoc +59 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +144 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +124 -0
- data/docs/guides/advanced-features/streaming.adoc +142 -0
- data/docs/guides/archive-formats/gzip-format.adoc +105 -0
- data/docs/guides/archive-formats/index.adoc +160 -0
- data/docs/guides/archive-formats/ole-format.adoc +151 -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/rpm-format.adoc +108 -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 +114 -0
- data/docs/guides/compression-algorithms/deflate.adoc +313 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +394 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +244 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +313 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +296 -0
- data/docs/guides/creating-archives.adoc +348 -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 +228 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +187 -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/rust-backend.md +0 -0
- data/docs/troubleshooting/index.adoc +207 -0
- data/docs/xar_format.md +216 -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 +176 -0
- data/lib/omnizip/algorithm_registry.rb +65 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +236 -0
- data/lib/omnizip/algorithms/bzip2/bz2.rb +687 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +146 -0
- data/lib/omnizip/algorithms/bzip2.rb +108 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +37 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +45 -0
- data/lib/omnizip/algorithms/deflate.rb +135 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +151 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +95 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +352 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +140 -0
- data/lib/omnizip/algorithms/deflate64.rb +118 -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 +146 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +70 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +326 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +139 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +172 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +204 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +208 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +368 -0
- data/lib/omnizip/algorithms/lzma/lzma1_encoder.rb +105 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +194 -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 +233 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +84 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +159 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +82 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +274 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +202 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +323 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +420 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +640 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +224 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +167 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +258 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +314 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +108 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +1311 -0
- data/lib/omnizip/algorithms/lzma.rb +266 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +180 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +38 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +145 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +165 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +122 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +133 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +151 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +127 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +153 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +245 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +146 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +32 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +104 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +135 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +246 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +122 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +142 -0
- data/lib/omnizip/algorithms/registration.rb +28 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +158 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +249 -0
- data/lib/omnizip/algorithms/zstandard/dictionary.rb +73 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +294 -0
- data/lib/omnizip/algorithms/zstandard/frame/block.rb +143 -0
- data/lib/omnizip/algorithms/zstandard/frame/header.rb +233 -0
- data/lib/omnizip/algorithms/zstandard/frame.rb +33 -0
- data/lib/omnizip/algorithms/zstandard/fse/bitstream.rb +183 -0
- data/lib/omnizip/algorithms/zstandard/fse/encoder.rb +534 -0
- data/lib/omnizip/algorithms/zstandard/fse/interleaved.rb +92 -0
- data/lib/omnizip/algorithms/zstandard/fse/table.rb +188 -0
- data/lib/omnizip/algorithms/zstandard/fse/table_description.rb +203 -0
- data/lib/omnizip/algorithms/zstandard/fse.rb +53 -0
- data/lib/omnizip/algorithms/zstandard/huffman.rb +276 -0
- data/lib/omnizip/algorithms/zstandard/huffman_encoder.rb +327 -0
- data/lib/omnizip/algorithms/zstandard/ldm.rb +121 -0
- data/lib/omnizip/algorithms/zstandard/literals.rb +241 -0
- data/lib/omnizip/algorithms/zstandard/literals_encoder.rb +96 -0
- data/lib/omnizip/algorithms/zstandard/match_finder.rb +483 -0
- data/lib/omnizip/algorithms/zstandard/sequences.rb +350 -0
- data/lib/omnizip/algorithms/zstandard/sequences_encoder.rb +333 -0
- data/lib/omnizip/algorithms/zstandard/xxhash.rb +159 -0
- data/lib/omnizip/algorithms/zstandard.rb +210 -0
- data/lib/omnizip/algorithms.rb +35 -0
- data/lib/omnizip/archive/builder.rb +48 -0
- data/lib/omnizip/archive/reader_session.rb +67 -0
- data/lib/omnizip/archive.rb +86 -0
- data/lib/omnizip/archive_handler.rb +82 -0
- data/lib/omnizip/archive_handlers/cpio_handler.rb +47 -0
- data/lib/omnizip/archive_handlers/iso_handler.rb +54 -0
- data/lib/omnizip/archive_handlers/ole_handler.rb +37 -0
- data/lib/omnizip/archive_handlers/rar_handler.rb +96 -0
- data/lib/omnizip/archive_handlers/rpm_handler.rb +45 -0
- data/lib/omnizip/archive_handlers/seven_zip_handler.rb +89 -0
- data/lib/omnizip/archive_handlers/tar_handler.rb +75 -0
- data/lib/omnizip/archive_handlers/xar_handler.rb +49 -0
- data/lib/omnizip/archive_handlers/zip_handler.rb +115 -0
- data/lib/omnizip/archive_handlers.rb +18 -0
- data/lib/omnizip/backends.rb +160 -0
- data/lib/omnizip/buffer/memory_archive.rb +246 -0
- data/lib/omnizip/buffer/memory_extractor.rb +234 -0
- data/lib/omnizip/buffer/seven_zip_bridge.rb +171 -0
- data/lib/omnizip/buffer.rb +187 -0
- data/lib/omnizip/checksum_registry.rb +41 -0
- data/lib/omnizip/checksums/crc32.rb +101 -0
- data/lib/omnizip/checksums/crc64.rb +102 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/registration.rb +16 -0
- data/lib/omnizip/checksums/verifier.rb +130 -0
- data/lib/omnizip/checksums.rb +11 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +123 -0
- data/lib/omnizip/chunked.rb +123 -0
- data/lib/omnizip/cli/output_formatter.rb +112 -0
- data/lib/omnizip/cli.rb +604 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +245 -0
- data/lib/omnizip/commands/archive_extract_command.rb +231 -0
- data/lib/omnizip/commands/archive_list_command.rb +204 -0
- data/lib/omnizip/commands/archive_repair_command.rb +129 -0
- data/lib/omnizip/commands/archive_verify_command.rb +115 -0
- data/lib/omnizip/commands/compress_command.rb +115 -0
- data/lib/omnizip/commands/decompress_command.rb +149 -0
- data/lib/omnizip/commands/list_command.rb +51 -0
- data/lib/omnizip/commands/metadata_command.rb +215 -0
- data/lib/omnizip/commands/parity_create_command.rb +114 -0
- data/lib/omnizip/commands/parity_repair_command.rb +120 -0
- data/lib/omnizip/commands/parity_verify_command.rb +122 -0
- data/lib/omnizip/commands/profile_list_command.rb +54 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/commands.rb +21 -0
- data/lib/omnizip/convenience.rb +389 -0
- data/lib/omnizip/converter/conversion_registry.rb +50 -0
- data/lib/omnizip/converter/conversion_strategy.rb +108 -0
- data/lib/omnizip/converter/extract_repack_strategy.rb +51 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +43 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +54 -0
- data/lib/omnizip/converter.rb +124 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +104 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +100 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/crypto.rb +11 -0
- data/lib/omnizip/entry.rb +44 -0
- data/lib/omnizip/error.rb +110 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +95 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +96 -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 +68 -0
- data/lib/omnizip/extraction/filter_chain.rb +179 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +66 -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 +247 -0
- data/lib/omnizip/extraction.rb +115 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +117 -0
- data/lib/omnizip/filter.rb +141 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +113 -0
- data/lib/omnizip/filters/bcj.rb +277 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +263 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +58 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +91 -0
- data/lib/omnizip/filters/bcj2.rb +93 -0
- data/lib/omnizip/filters/bcj_arm.rb +174 -0
- data/lib/omnizip/filters/bcj_arm64.rb +242 -0
- data/lib/omnizip/filters/bcj_ia64.rb +194 -0
- data/lib/omnizip/filters/bcj_ppc.rb +188 -0
- data/lib/omnizip/filters/bcj_sparc.rb +174 -0
- data/lib/omnizip/filters/bcj_x86.rb +191 -0
- data/lib/omnizip/filters/delta.rb +195 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/filters.rb +20 -0
- data/lib/omnizip/format_detector.rb +166 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +159 -0
- data/lib/omnizip/formats/cpio/bounded_io.rb +66 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +389 -0
- data/lib/omnizip/formats/cpio/reader.rb +194 -0
- data/lib/omnizip/formats/cpio/writer.rb +232 -0
- data/lib/omnizip/formats/cpio.rb +132 -0
- data/lib/omnizip/formats/format_spec_loader.rb +228 -0
- data/lib/omnizip/formats/gzip.rb +236 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +321 -0
- data/lib/omnizip/formats/iso/directory_record.rb +162 -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 +194 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +319 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +543 -0
- data/lib/omnizip/formats/iso.rb +133 -0
- data/lib/omnizip/formats/lzip.rb +161 -0
- data/lib/omnizip/formats/lzma_alone.rb +149 -0
- data/lib/omnizip/formats/msi/cab_extractor.rb +181 -0
- data/lib/omnizip/formats/msi/constants.rb +157 -0
- data/lib/omnizip/formats/msi/directory_resolver.rb +188 -0
- data/lib/omnizip/formats/msi/entry.rb +134 -0
- data/lib/omnizip/formats/msi/reader.rb +301 -0
- data/lib/omnizip/formats/msi/string_pool.rb +246 -0
- data/lib/omnizip/formats/msi/table_parser.rb +244 -0
- data/lib/omnizip/formats/msi.rb +73 -0
- data/lib/omnizip/formats/ole/allocation_table.rb +263 -0
- data/lib/omnizip/formats/ole/constants.rb +61 -0
- data/lib/omnizip/formats/ole/dirent.rb +439 -0
- data/lib/omnizip/formats/ole/header.rb +199 -0
- data/lib/omnizip/formats/ole/ranges_io.rb +347 -0
- data/lib/omnizip/formats/ole/storage.rb +482 -0
- data/lib/omnizip/formats/ole/types/variant.rb +338 -0
- data/lib/omnizip/formats/ole.rb +143 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +240 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +192 -0
- data/lib/omnizip/formats/rar/block_parser.rb +282 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +210 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +212 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +154 -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/lz77_huffman.rb +25 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +103 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +238 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +250 -0
- data/lib/omnizip/formats/rar/compression/ppmd.rb +16 -0
- data/lib/omnizip/formats/rar/compression.rb +17 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +88 -0
- data/lib/omnizip/formats/rar/decompressor.rb +316 -0
- data/lib/omnizip/formats/rar/external_writer.rb +311 -0
- data/lib/omnizip/formats/rar/header.rb +209 -0
- data/lib/omnizip/formats/rar/license_validator.rb +111 -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/models.rb +14 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +299 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +200 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +584 -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 +96 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +162 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +208 -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 +260 -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 +176 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +108 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +139 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +53 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +540 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +239 -0
- data/lib/omnizip/formats/rar/reader.rb +377 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +166 -0
- data/lib/omnizip/formats/rar/writer.rb +437 -0
- data/lib/omnizip/formats/rar.rb +193 -0
- data/lib/omnizip/formats/rar3/compressor.rb +71 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +64 -0
- data/lib/omnizip/formats/rar3/reader.rb +107 -0
- data/lib/omnizip/formats/rar3/writer.rb +220 -0
- data/lib/omnizip/formats/rar3.rb +15 -0
- data/lib/omnizip/formats/rar5/compressor.rb +71 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +64 -0
- data/lib/omnizip/formats/rar5/reader.rb +130 -0
- data/lib/omnizip/formats/rar5/writer.rb +65 -0
- data/lib/omnizip/formats/rar5.rb +15 -0
- data/lib/omnizip/formats/rpm/constants.rb +58 -0
- data/lib/omnizip/formats/rpm/entry.rb +106 -0
- data/lib/omnizip/formats/rpm/header.rb +106 -0
- data/lib/omnizip/formats/rpm/lead.rb +132 -0
- data/lib/omnizip/formats/rpm/tag.rb +228 -0
- data/lib/omnizip/formats/rpm/writer.rb +652 -0
- data/lib/omnizip/formats/rpm.rb +458 -0
- data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +274 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +169 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +110 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +139 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +143 -0
- data/lib/omnizip/formats/seven_zip/header.rb +102 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +132 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +425 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +65 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +67 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/models.rb +20 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +671 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +626 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +611 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +308 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +166 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +801 -0
- data/lib/omnizip/formats/seven_zip.rb +179 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +98 -0
- data/lib/omnizip/formats/tar/header.rb +206 -0
- data/lib/omnizip/formats/tar/reader.rb +119 -0
- data/lib/omnizip/formats/tar/writer.rb +212 -0
- data/lib/omnizip/formats/tar.rb +74 -0
- data/lib/omnizip/formats/xar/constants.rb +119 -0
- data/lib/omnizip/formats/xar/entry.rb +268 -0
- data/lib/omnizip/formats/xar/header.rb +223 -0
- data/lib/omnizip/formats/xar/reader.rb +400 -0
- data/lib/omnizip/formats/xar/toc.rb +570 -0
- data/lib/omnizip/formats/xar/writer.rb +461 -0
- data/lib/omnizip/formats/xar.rb +145 -0
- data/lib/omnizip/formats/xz/reader.rb +121 -0
- data/lib/omnizip/formats/xz/writer.rb +416 -0
- data/lib/omnizip/formats/xz.rb +210 -0
- data/lib/omnizip/formats/xz_const.rb +49 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +761 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +304 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +182 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +120 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +470 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +95 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +79 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +115 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +53 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +106 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl.rb +52 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +223 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +132 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +136 -0
- data/lib/omnizip/formats/zip/reader.rb +376 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +489 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +102 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +64 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +112 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/formats.rb +29 -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/base.rb +19 -0
- data/lib/omnizip/implementations/rust/archive.rb +93 -0
- data/lib/omnizip/implementations/rust/library.rb +221 -0
- data/lib/omnizip/implementations/rust.rb +26 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +428 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +456 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +285 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +139 -0
- data/lib/omnizip/implementations/seven_zip/lzma.rb +21 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +56 -0
- data/lib/omnizip/implementations/seven_zip/lzma2.rb +12 -0
- data/lib/omnizip/implementations/seven_zip.rb +11 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +547 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +748 -0
- data/lib/omnizip/implementations/xz_utils/lzma2.rb +13 -0
- data/lib/omnizip/implementations/xz_utils.rb +10 -0
- data/lib/omnizip/implementations.rb +25 -0
- data/lib/omnizip/io/buffered_input.rb +147 -0
- data/lib/omnizip/io/buffered_output.rb +106 -0
- data/lib/omnizip/io/source.rb +143 -0
- data/lib/omnizip/io/stream_manager.rb +117 -0
- data/lib/omnizip/io.rb +15 -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 +129 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +153 -0
- data/lib/omnizip/metadata/metadata_editor.rb +169 -0
- data/lib/omnizip/metadata/metadata_registry.rb +71 -0
- data/lib/omnizip/metadata/metadata_validator.rb +101 -0
- data/lib/omnizip/metadata.rb +58 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +41 -0
- data/lib/omnizip/models/attribute_apply.rb +40 -0
- data/lib/omnizip/models/compression_options.rb +53 -0
- data/lib/omnizip/models/conversion_options.rb +79 -0
- data/lib/omnizip/models/conversion_result.rb +141 -0
- data/lib/omnizip/models/eta_result.rb +45 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +142 -0
- data/lib/omnizip/models/filter_config.rb +148 -0
- data/lib/omnizip/models/match_result.rb +141 -0
- data/lib/omnizip/models/optimization_suggestion.rb +95 -0
- data/lib/omnizip/models/parallel_options.rb +116 -0
- data/lib/omnizip/models/performance_result.rb +85 -0
- data/lib/omnizip/models/profile_report.rb +116 -0
- data/lib/omnizip/models/progress_options.rb +39 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +45 -0
- data/lib/omnizip/parallel/engine.rb +52 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +225 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +326 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +238 -0
- data/lib/omnizip/parallel/thread_pool.rb +60 -0
- data/lib/omnizip/parallel/worker_pool.rb +227 -0
- data/lib/omnizip/parallel.rb +156 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +194 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +71 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +131 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +111 -0
- data/lib/omnizip/parity/models/main_packet.rb +126 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +102 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +76 -0
- data/lib/omnizip/parity/models.rb +27 -0
- data/lib/omnizip/parity/par2_creator.rb +496 -0
- data/lib/omnizip/parity/par2_repairer.rb +409 -0
- data/lib/omnizip/parity/par2_verifier.rb +373 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +108 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +263 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +108 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +339 -0
- data/lib/omnizip/parity.rb +207 -0
- data/lib/omnizip/password/encryption_registry.rb +26 -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 +190 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +135 -0
- data/lib/omnizip/password.rb +88 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +173 -0
- data/lib/omnizip/pipe.rb +120 -0
- data/lib/omnizip/platform/ntfs_streams.rb +200 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +36 -0
- data/lib/omnizip/profile/balanced_profile.rb +31 -0
- data/lib/omnizip/profile/binary_profile.rb +33 -0
- data/lib/omnizip/profile/compression_profile.rb +157 -0
- data/lib/omnizip/profile/custom_profile.rb +155 -0
- data/lib/omnizip/profile/fast_profile.rb +31 -0
- data/lib/omnizip/profile/maximum_profile.rb +31 -0
- data/lib/omnizip/profile/profile_detector.rb +108 -0
- data/lib/omnizip/profile/profile_registry.rb +160 -0
- data/lib/omnizip/profile/text_profile.rb +33 -0
- data/lib/omnizip/profile.rb +261 -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 +158 -0
- data/lib/omnizip/profiler.rb +192 -0
- data/lib/omnizip/progress/callback_reporter.rb +34 -0
- data/lib/omnizip/progress/console_reporter.rb +59 -0
- data/lib/omnizip/progress/log_reporter.rb +89 -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 +215 -0
- data/lib/omnizip/progress/silent_reporter.rb +22 -0
- data/lib/omnizip/progress.rb +131 -0
- data/lib/omnizip/registry.rb +111 -0
- data/lib/omnizip/rubyzip_compat.rb +61 -0
- data/lib/omnizip/temp/safe_extract.rb +124 -0
- data/lib/omnizip/temp/temp_file.rb +123 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +184 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +160 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +270 -0
- data/lib/omnizip/zip/output_stream.rb +320 -0
- data/lib/omnizip/zip.rb +23 -0
- data/lib/omnizip.rb +157 -0
- data/lib/tasks/msi_test_files.rake +66 -0
- data/lib/tasks/platform_gems.rake +147 -0
- data/lib/tasks/rust_backend.rake +48 -0
- data/readme-docs/advanced-features.adoc +514 -0
- data/readme-docs/api-usage.adoc +445 -0
- data/readme-docs/architecture.adoc +450 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +446 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +316 -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/vendor/libomnizip_ffi.so +0 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +768 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
6
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
7
|
+
# to deal in the Software without restriction, including without limitation
|
|
8
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
9
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
10
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
21
|
+
# DEALINGS IN THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
require "stringio"
|
|
24
|
+
|
|
25
|
+
module Omnizip
|
|
26
|
+
module Algorithms
|
|
27
|
+
class LZMA < Algorithm
|
|
28
|
+
#
|
|
29
|
+
# Decoder for .lz (lzip) format
|
|
30
|
+
#
|
|
31
|
+
# This is the lzip format, a DIFFERENT container format from both
|
|
32
|
+
# XZ and .lzma (LZMA_Alone). Lzip was created as an alternative to
|
|
33
|
+
# the legacy .lzma format with better integrity checking.
|
|
34
|
+
#
|
|
35
|
+
# File format:
|
|
36
|
+
# - Magic bytes: "LZIP" (0x4C 0x5A 0x49 0x50)
|
|
37
|
+
# - Version (1 byte): 0 or 1
|
|
38
|
+
# - Dictionary size (1 byte): encoded format
|
|
39
|
+
# - LZMA1 compressed stream (with fixed LC=3, LP=0, PB=2)
|
|
40
|
+
# - Footer:
|
|
41
|
+
# - Version 0 (12 bytes): CRC32 (4) + Uncompressed size (8)
|
|
42
|
+
# - Version 1 (20 bytes): CRC32 (4) + Uncompressed size (8) + Member size (8)
|
|
43
|
+
#
|
|
44
|
+
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/lzip_decoder.c
|
|
45
|
+
#
|
|
46
|
+
# This decoder uses the same LZMA1 decoding engine as XZ format,
|
|
47
|
+
# but with the lzip container format and CRC32 integrity checking.
|
|
48
|
+
#
|
|
49
|
+
# @example Decode .lz file
|
|
50
|
+
# data = File.binread("file.lz")
|
|
51
|
+
# decoder = Omnizip::Algorithms::LZMA::LzipDecoder.new(StringIO.new(data))
|
|
52
|
+
# result = decoder.decode_stream
|
|
53
|
+
#
|
|
54
|
+
class LzipDecoder
|
|
55
|
+
attr_reader :version, :dict_size, :member_size
|
|
56
|
+
|
|
57
|
+
# Lzip magic bytes: "LZIP" in ASCII
|
|
58
|
+
# Reference: lzip_decoder.c:106
|
|
59
|
+
MAGIC = [0x4C, 0x5A, 0x49, 0x50].freeze
|
|
60
|
+
|
|
61
|
+
# Fixed LC/LP/PB values for lzip format
|
|
62
|
+
# Reference: lzip_decoder.c:23-26
|
|
63
|
+
LZIP_LC = 3
|
|
64
|
+
LZIP_LP = 0
|
|
65
|
+
LZIP_PB = 2
|
|
66
|
+
|
|
67
|
+
# Footer sizes
|
|
68
|
+
# Reference: lzip_decoder.c:19-21
|
|
69
|
+
LZIP_V0_FOOTER_SIZE = 12
|
|
70
|
+
LZIP_V1_FOOTER_SIZE = 20
|
|
71
|
+
LZIP_FOOTER_SIZE_MAX = LZIP_V1_FOOTER_SIZE
|
|
72
|
+
|
|
73
|
+
# Minimum and maximum dictionary sizes (in bytes)
|
|
74
|
+
# Reference: lzip_decoder.c:197-198
|
|
75
|
+
MIN_DICT_SIZE = 4096 # 4 KiB
|
|
76
|
+
MAX_DICT_SIZE = (512 << 20) # 512 MiB
|
|
77
|
+
|
|
78
|
+
# Initialize the decoder with .lz format input
|
|
79
|
+
#
|
|
80
|
+
# @param input [IO] Input stream of .lz compressed data
|
|
81
|
+
# @param options [Hash] Decoding options
|
|
82
|
+
# @option options [Boolean] :ignore_check If true, skip CRC32 verification (default: false)
|
|
83
|
+
# @option options [Boolean] :concatenated If true, decode concatenated .lz members (default: false)
|
|
84
|
+
# @raise [Omnizip::DecompressionError] If header is invalid or unsupported
|
|
85
|
+
def initialize(input, options = {})
|
|
86
|
+
@input = input
|
|
87
|
+
@ignore_check = options.fetch(:ignore_check, false)
|
|
88
|
+
# Concatenated mode is enabled by default (lzip natively supports multiple members)
|
|
89
|
+
@concatenated = options.fetch(:concatenated, true)
|
|
90
|
+
|
|
91
|
+
# Parse .lz header
|
|
92
|
+
parse_header
|
|
93
|
+
|
|
94
|
+
# Track member size (including header and footer)
|
|
95
|
+
# We start with the 6 bytes we've already read (magic + version + dict_size)
|
|
96
|
+
@member_size = 6
|
|
97
|
+
|
|
98
|
+
# For concatenated mode, track if this is the first member
|
|
99
|
+
@first_member = true
|
|
100
|
+
|
|
101
|
+
# Initialize CRC32 calculator
|
|
102
|
+
@crc32 = 0
|
|
103
|
+
@uncompressed_size = 0
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Decode the .lz stream
|
|
107
|
+
#
|
|
108
|
+
# @param output [IO, nil] Optional output stream
|
|
109
|
+
# @return [String, Integer] Decompressed data or bytes written
|
|
110
|
+
def decode_stream(output = nil)
|
|
111
|
+
# For concatenated mode, accumulate all decoded data
|
|
112
|
+
all_decoded_data = String.new(encoding: Encoding::BINARY)
|
|
113
|
+
bytes_written = 0
|
|
114
|
+
result = nil # Initialize result variable
|
|
115
|
+
|
|
116
|
+
loop do
|
|
117
|
+
# Track the starting position of compressed data
|
|
118
|
+
start_pos = @input.pos
|
|
119
|
+
|
|
120
|
+
# Initialize the XZ Utils LZMA decoder with fixed lzip parameters
|
|
121
|
+
decoder = XzUtilsDecoder.new(@input,
|
|
122
|
+
lzma2_mode: true,
|
|
123
|
+
lc: LZIP_LC,
|
|
124
|
+
lp: LZIP_LP,
|
|
125
|
+
pb: LZIP_PB,
|
|
126
|
+
dict_size: @dict_size,
|
|
127
|
+
uncompressed_size: 0xFFFFFFFFFFFFFFFF) # Unknown size, allow EOPM
|
|
128
|
+
|
|
129
|
+
# Decode the LZMA stream (allow EOPM for .lz format)
|
|
130
|
+
# Get decoded data as string (no output stream)
|
|
131
|
+
decoded_data = decoder.decode_stream(nil, check_rc_finished: false)
|
|
132
|
+
|
|
133
|
+
# If caller provided output stream, write to it
|
|
134
|
+
if output
|
|
135
|
+
output.write(decoded_data)
|
|
136
|
+
bytes_written += decoded_data.bytesize
|
|
137
|
+
result = bytes_written
|
|
138
|
+
else
|
|
139
|
+
all_decoded_data << decoded_data
|
|
140
|
+
result = all_decoded_data
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Calculate member size (header + compressed data + footer)
|
|
144
|
+
# We calculate it here (compressed data + header), then add footer size below
|
|
145
|
+
@member_size = @input.pos - start_pos + 6 # +6 for header bytes
|
|
146
|
+
|
|
147
|
+
# Calculate and verify CRC32
|
|
148
|
+
if @ignore_check
|
|
149
|
+
# Skip footer
|
|
150
|
+
footer_size = @version.zero? ? LZIP_V0_FOOTER_SIZE : LZIP_V1_FOOTER_SIZE
|
|
151
|
+
@input.read(footer_size)
|
|
152
|
+
@member_size += footer_size
|
|
153
|
+
else
|
|
154
|
+
data_to_crc = decoded_data || +""
|
|
155
|
+
calculated_crc = Omnizip::Checksums::Crc32.calculate(data_to_crc)
|
|
156
|
+
@uncompressed_size = data_to_crc.bytesize
|
|
157
|
+
|
|
158
|
+
# Read and verify footer (also updates @member_size to include footer)
|
|
159
|
+
verify_footer(calculated_crc)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Check for concatenated members
|
|
163
|
+
break unless @concatenated
|
|
164
|
+
|
|
165
|
+
# Peek ahead to check if there's another LZIP member
|
|
166
|
+
break unless has_next_member?
|
|
167
|
+
|
|
168
|
+
# Parse header for next member
|
|
169
|
+
parse_header
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Return decoded data or bytes written
|
|
173
|
+
result
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
# Check if there's another concatenated LZIP member
|
|
179
|
+
# Peeks ahead without consuming the magic bytes
|
|
180
|
+
#
|
|
181
|
+
# @return [Boolean] true if another member is present
|
|
182
|
+
def has_next_member?
|
|
183
|
+
# Peek at next 4 bytes to check for magic
|
|
184
|
+
magic_bytes = @input.read(4)
|
|
185
|
+
return false if magic_bytes.nil? || magic_bytes.bytesize < 4
|
|
186
|
+
|
|
187
|
+
# Check if it's LZIP magic
|
|
188
|
+
is_lzip = magic_bytes.getbyte(0) == MAGIC[0] &&
|
|
189
|
+
magic_bytes.getbyte(1) == MAGIC[1] &&
|
|
190
|
+
magic_bytes.getbyte(2) == MAGIC[2] &&
|
|
191
|
+
magic_bytes.getbyte(3) == MAGIC[3]
|
|
192
|
+
|
|
193
|
+
# Put the bytes back by seeking back
|
|
194
|
+
@input.seek(-4, ::IO::SEEK_CUR) if is_lzip
|
|
195
|
+
|
|
196
|
+
is_lzip
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Parse .lz format header
|
|
200
|
+
#
|
|
201
|
+
# Format (from lzip_decoder.c):
|
|
202
|
+
# - Magic bytes: "LZIP" (4 bytes)
|
|
203
|
+
# - Version (1 byte): 0 or 1
|
|
204
|
+
# - Dictionary size (1 byte): encoded format
|
|
205
|
+
#
|
|
206
|
+
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/lzip_decoder.c
|
|
207
|
+
#
|
|
208
|
+
# @return [void]
|
|
209
|
+
# @raise [Omnizip::DecompressionError] If header is invalid
|
|
210
|
+
def parse_header
|
|
211
|
+
# Step 1: Verify magic bytes (SEQ_ID_STRING)
|
|
212
|
+
# Reference: lzip_decoder.c:104-153
|
|
213
|
+
magic_bytes = @input.read(4)
|
|
214
|
+
if magic_bytes.nil? || magic_bytes.bytesize < 4
|
|
215
|
+
raise Omnizip::DecompressionError,
|
|
216
|
+
"Incomplete .lz header: missing magic bytes"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
4.times do |i|
|
|
220
|
+
if magic_bytes.getbyte(i) != MAGIC[i]
|
|
221
|
+
raise Omnizip::DecompressionError, "Invalid .lz header: magic bytes don't match LZIP (expected #{MAGIC.map do |b|
|
|
222
|
+
"0x#{b.to_s(16).upcase}"
|
|
223
|
+
end.join(' ')}, got #{magic_bytes.bytes.map do |b|
|
|
224
|
+
"0x#{b.to_s(16).upcase}"
|
|
225
|
+
end.join(' ')})"
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Step 2: Read version byte (SEQ_VERSION)
|
|
230
|
+
# Reference: lzip_decoder.c:156-174
|
|
231
|
+
version_byte = @input.getbyte
|
|
232
|
+
if version_byte.nil?
|
|
233
|
+
raise Omnizip::DecompressionError,
|
|
234
|
+
"Incomplete .lz header: missing version byte"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
@version = version_byte
|
|
238
|
+
|
|
239
|
+
# We support version 0 and unextended version 1
|
|
240
|
+
# Reference: lzip_decoder.c:163-164
|
|
241
|
+
if @version > 1
|
|
242
|
+
raise Omnizip::UnsupportedFormatError,
|
|
243
|
+
"Unsupported .lz version: #{@version} (only 0 and 1 are supported)"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Step 3: Parse dictionary size (SEQ_DICT_SIZE)
|
|
247
|
+
# Reference: lzip_decoder.c:177-222
|
|
248
|
+
dict_size_byte = @input.getbyte
|
|
249
|
+
if dict_size_byte.nil?
|
|
250
|
+
raise Omnizip::DecompressionError,
|
|
251
|
+
"Incomplete .lz header: missing dictionary size byte"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Decode dictionary size from the encoded byte
|
|
255
|
+
# The five lowest bits are for the base-2 logarithm of the dictionary size
|
|
256
|
+
# and the highest three bits are the fractional part (0/16 to 7/16)
|
|
257
|
+
# Reference: lzip_decoder.c:183-204
|
|
258
|
+
b2log = dict_size_byte & 0x1F
|
|
259
|
+
fracnum = dict_size_byte >> 5
|
|
260
|
+
|
|
261
|
+
# Validate range: [4 KiB, 512 MiB]
|
|
262
|
+
# Reference: lzip_decoder.c:198-199
|
|
263
|
+
if b2log < 12 || b2log > 29 || (b2log == 12 && fracnum.positive?)
|
|
264
|
+
raise Omnizip::DecompressionError,
|
|
265
|
+
"Invalid .lz header: dictionary size byte 0x#{dict_size_byte.to_s(16).upcase} is out of valid range"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Calculate: 2^[b2log] - [fracnum] * 2^([b2log] - 4)
|
|
269
|
+
# Reference: lzip_decoder.c:201-204
|
|
270
|
+
@dict_size = (1 << b2log) - (fracnum << (b2log - 4))
|
|
271
|
+
|
|
272
|
+
# Sanity checks
|
|
273
|
+
if @dict_size < MIN_DICT_SIZE
|
|
274
|
+
raise Omnizip::DecompressionError,
|
|
275
|
+
"Dictionary size calculation error: too small"
|
|
276
|
+
end
|
|
277
|
+
if @dict_size > MAX_DICT_SIZE
|
|
278
|
+
raise Omnizip::DecompressionError,
|
|
279
|
+
"Dictionary size calculation error: too large"
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Verify .lz format footer
|
|
284
|
+
#
|
|
285
|
+
# Format (from lzip_decoder.c):
|
|
286
|
+
# - CRC32 of uncompressed data (4 bytes, little-endian)
|
|
287
|
+
# - Uncompressed size (8 bytes, little-endian)
|
|
288
|
+
# - Member size (8 bytes, little-endian) - only for version 1
|
|
289
|
+
#
|
|
290
|
+
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/lzip_decoder.c:277-309
|
|
291
|
+
#
|
|
292
|
+
# @param calculated_crc [Integer] Calculated CRC32 of uncompressed data
|
|
293
|
+
# @raise [Omnizip::DecompressionError] If footer is invalid or checksum mismatch
|
|
294
|
+
def verify_footer(calculated_crc)
|
|
295
|
+
footer_size = @version.zero? ? LZIP_V0_FOOTER_SIZE : LZIP_V1_FOOTER_SIZE
|
|
296
|
+
footer = @input.read(footer_size)
|
|
297
|
+
if footer.nil? || footer.bytesize < footer_size
|
|
298
|
+
raise Omnizip::DecompressionError,
|
|
299
|
+
"Incomplete .lz footer: expected #{footer_size} bytes, got #{footer&.bytesize || 0}"
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Update member_size to include the footer
|
|
303
|
+
@member_size += footer_size
|
|
304
|
+
|
|
305
|
+
# Parse CRC32 (little-endian)
|
|
306
|
+
stored_crc = footer.getbyte(0) | (footer.getbyte(1) << 8) |
|
|
307
|
+
(footer.getbyte(2) << 16) | (footer.getbyte(3) << 24)
|
|
308
|
+
|
|
309
|
+
# Verify CRC32
|
|
310
|
+
if calculated_crc != stored_crc
|
|
311
|
+
raise Omnizip::ChecksumError,
|
|
312
|
+
"CRC32 mismatch: calculated 0x#{calculated_crc.to_s(16).upcase}, stored 0x#{stored_crc.to_s(16).upcase}"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Parse and verify uncompressed size (little-endian)
|
|
316
|
+
stored_uncompressed_size = footer.getbyte(4) | (footer.getbyte(5) << 8) |
|
|
317
|
+
(footer.getbyte(6) << 16) | (footer.getbyte(7) << 24) |
|
|
318
|
+
(footer.getbyte(8) << 32) | (footer.getbyte(9) << 40) |
|
|
319
|
+
(footer.getbyte(10) << 48) | (footer.getbyte(11) << 56)
|
|
320
|
+
|
|
321
|
+
if @uncompressed_size != stored_uncompressed_size
|
|
322
|
+
raise Omnizip::ChecksumError,
|
|
323
|
+
"Uncompressed size mismatch: decoded #{@uncompressed_size}, stored #{stored_uncompressed_size}"
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# For version 1, verify member size
|
|
327
|
+
if @version.positive?
|
|
328
|
+
stored_member_size = footer.getbyte(12) | (footer.getbyte(13) << 8) |
|
|
329
|
+
(footer.getbyte(14) << 16) | (footer.getbyte(15) << 24) |
|
|
330
|
+
(footer.getbyte(16) << 32) | (footer.getbyte(17) << 40) |
|
|
331
|
+
(footer.getbyte(18) << 48) | (footer.getbyte(19) << 56)
|
|
332
|
+
|
|
333
|
+
if @member_size != stored_member_size
|
|
334
|
+
raise Omnizip::ChecksumError,
|
|
335
|
+
"Member size mismatch: decoded #{@member_size}, stored #{stored_member_size}"
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Wrapper input stream that tracks bytes read
|
|
341
|
+
class TrackingInputStream
|
|
342
|
+
attr_reader :bytes_read
|
|
343
|
+
|
|
344
|
+
def initialize(input, start_offset = 0)
|
|
345
|
+
@input = input
|
|
346
|
+
@bytes_read = start_offset
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def read(size = nil)
|
|
350
|
+
data = @input.read(size)
|
|
351
|
+
@bytes_read += data.bytesize if data
|
|
352
|
+
data
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def getbyte
|
|
356
|
+
byte = @input.getbyte
|
|
357
|
+
@bytes_read += 1 if byte
|
|
358
|
+
byte
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def eof?
|
|
362
|
+
@input.eof?
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
5
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
8
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
9
|
+
# to deal in the Software without restriction, including without limitation
|
|
10
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
11
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
12
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
|
15
|
+
# all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
22
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
23
|
+
# DEALINGS IN THE SOFTWARE.
|
|
24
|
+
|
|
25
|
+
module Omnizip
|
|
26
|
+
module Algorithms
|
|
27
|
+
class LZMA
|
|
28
|
+
# Raw LZMA1 stream encoder (one continuous range-coded stream
|
|
29
|
+
# with the end-of-stream marker), reusing the XZ Utils symbol
|
|
30
|
+
# coders. This is the payload for the legacy .lzma container
|
|
31
|
+
# and the lzip member body.
|
|
32
|
+
class Lzma1Encoder < Implementations::XZUtils::LZMA2::Encoder
|
|
33
|
+
UINT32_MAX = 0xFFFFFFFF
|
|
34
|
+
REPS = 4
|
|
35
|
+
|
|
36
|
+
# Encode `input` as a complete standalone LZMA1 stream,
|
|
37
|
+
# terminated by the end-of-stream marker (dist = 0xFFFFFFFF,
|
|
38
|
+
# length 2) so decoders that do not know the size can stop.
|
|
39
|
+
#
|
|
40
|
+
# @param input [String]
|
|
41
|
+
# @param emit_eopm [Boolean] omit when the container carries
|
|
42
|
+
# the uncompressed size (the legacy .lzma form)
|
|
43
|
+
# @return [String] binary stream
|
|
44
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
45
|
+
def encode(input, emit_eopm: true)
|
|
46
|
+
@match_finder.reset
|
|
47
|
+
output_buffer = StringIO.new
|
|
48
|
+
output_buffer.set_encoding(Encoding::BINARY)
|
|
49
|
+
encoder = XzRangeEncoder.new(output_buffer)
|
|
50
|
+
@match_finder.feed(input)
|
|
51
|
+
@match_finder.skip(@match_finder.buffer.bytesize)
|
|
52
|
+
|
|
53
|
+
start_pos = 0
|
|
54
|
+
@current_start_pos = start_pos
|
|
55
|
+
pos = 0
|
|
56
|
+
while pos < input.bytesize
|
|
57
|
+
encode_queued_symbols(encoder, output_buffer)
|
|
58
|
+
|
|
59
|
+
match_pos = start_pos + pos
|
|
60
|
+
distance, length = @optimal.find_optimal(
|
|
61
|
+
match_pos, @match_finder, @state, @state.reps, @models
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if distance == UINT32_MAX || length == 1
|
|
65
|
+
encode_literal(input.getbyte(pos), encoder, match_pos)
|
|
66
|
+
pos += 1
|
|
67
|
+
elsif distance < REPS
|
|
68
|
+
encode_repeated_match(distance, length, encoder, match_pos,
|
|
69
|
+
match_pos)
|
|
70
|
+
pos += length
|
|
71
|
+
else
|
|
72
|
+
encode_match(distance - REPS, length, encoder, match_pos,
|
|
73
|
+
match_pos, input)
|
|
74
|
+
pos += length
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
encode_eopm(encoder, output_buffer, input.bytesize) if emit_eopm
|
|
79
|
+
encode_queued_symbols(encoder, output_buffer)
|
|
80
|
+
encoder.queue_flush
|
|
81
|
+
encode_queued_symbols(encoder, output_buffer)
|
|
82
|
+
output_buffer.string
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# End-of-stream marker: a normal match header with
|
|
88
|
+
# dist0 = 0xFFFFFFFF and length 2. The distance slot is 63,
|
|
89
|
+
# whose 30 footer bits the parent's distance coder emits as
|
|
90
|
+
# 26 direct bits + the 4 align bits.
|
|
91
|
+
def encode_eopm(encoder, output_buffer, total_pos)
|
|
92
|
+
pos_state = total_pos & ((1 << @pb) - 1)
|
|
93
|
+
encoder.queue_bit(@models.is_match[@state.value][pos_state], 1)
|
|
94
|
+
encoder.queue_bit(@models.is_rep[@state.value], 0)
|
|
95
|
+
@state.update_match!(UINT32_MAX)
|
|
96
|
+
encode_match_length(2, pos_state, encoder)
|
|
97
|
+
# Slot 63's 30 footer bits plus the length/header symbols
|
|
98
|
+
# exceed the 53-slot symbol queue in one go.
|
|
99
|
+
encode_queued_symbols(encoder, output_buffer)
|
|
100
|
+
encode_distance(UINT32_MAX + 1, 2, encoder)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
6
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
7
|
+
# to deal in the Software without restriction, including without limitation
|
|
8
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
9
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
10
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
21
|
+
# DEALINGS IN THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
require "stringio"
|
|
24
|
+
|
|
25
|
+
module Omnizip
|
|
26
|
+
module Algorithms
|
|
27
|
+
class LZMA < Algorithm
|
|
28
|
+
#
|
|
29
|
+
# Decoder for .lzma (LZMA_Alone) format
|
|
30
|
+
#
|
|
31
|
+
# This is the legacy LZMA_Alone format used by LZMA Utils 4.32.x.
|
|
32
|
+
# It is DIFFERENT from the XZ format's LZMA2 compression!
|
|
33
|
+
#
|
|
34
|
+
# File format:
|
|
35
|
+
# - Properties (1 byte): encodes lc, lp, pb values
|
|
36
|
+
# - Dictionary size (4 bytes, little-endian)
|
|
37
|
+
# - Uncompressed size (8 bytes, little-endian, UINT64_MAX = unknown)
|
|
38
|
+
# - LZMA1 compressed stream (no footer, no CRC32)
|
|
39
|
+
#
|
|
40
|
+
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/alone_decoder.c
|
|
41
|
+
#
|
|
42
|
+
# This decoder uses the same LZMA1 decoding engine as XZ format,
|
|
43
|
+
# but with the legacy .lzma container format.
|
|
44
|
+
#
|
|
45
|
+
# @example Decode .lzma file
|
|
46
|
+
# data = File.binread("file.lzma")
|
|
47
|
+
# decoder = Omnizip::Algorithms::LZMA::LzmaAloneDecoder.new(StringIO.new(data))
|
|
48
|
+
# result = decoder.decode_stream
|
|
49
|
+
#
|
|
50
|
+
class LzmaAloneDecoder
|
|
51
|
+
# Maximum valid uncompressed size (256 GiB)
|
|
52
|
+
# From alone_decoder.c:118
|
|
53
|
+
MAX_UNCOMPRESSED_SIZE = (1 << 38)
|
|
54
|
+
|
|
55
|
+
# Property byte validation limits
|
|
56
|
+
# From lzma_decoder.c:1218
|
|
57
|
+
MAX_PROPERTY_BYTE = (((4 * 5) + 4) * 9) + 8 # = 233
|
|
58
|
+
|
|
59
|
+
# Header fields exposed to container callers.
|
|
60
|
+
attr_reader :lc, :lp, :pb, :dict_size, :uncompressed_size
|
|
61
|
+
|
|
62
|
+
# Initialize the decoder with .lzma format input
|
|
63
|
+
#
|
|
64
|
+
# @param input [IO] Input stream of .lzma compressed data
|
|
65
|
+
# @param options [Hash] Decoding options
|
|
66
|
+
# @option options [Boolean] :picky If true, reject files unlikely to be .lzma (default: false)
|
|
67
|
+
# @raise [RuntimeError] If header is invalid or unsupported
|
|
68
|
+
def initialize(input, options = {})
|
|
69
|
+
@input = input
|
|
70
|
+
@picky = options.fetch(:picky, false)
|
|
71
|
+
|
|
72
|
+
# Parse .lzma header
|
|
73
|
+
parse_header
|
|
74
|
+
|
|
75
|
+
# Create a wrapper stream that starts after the header
|
|
76
|
+
# The XzUtilsDecoder will read from this stream
|
|
77
|
+
@lzma_stream = @input
|
|
78
|
+
|
|
79
|
+
# Initialize the XZ Utils LZMA decoder with parsed parameters
|
|
80
|
+
# validate_size=true because .lzma format has explicit uncompressed size
|
|
81
|
+
# allow_eopm=true because .lzma format allows EOPM even with known size
|
|
82
|
+
# Reference: alone_decoder.c:127 (LZMA_LZMA1EXT_ALLOW_EOPM)
|
|
83
|
+
@decoder = XzUtilsDecoder.new(@lzma_stream,
|
|
84
|
+
lzma2_mode: true,
|
|
85
|
+
validate_size: true,
|
|
86
|
+
allow_eopm: true,
|
|
87
|
+
lc: @lc,
|
|
88
|
+
lp: @lp,
|
|
89
|
+
pb: @pb,
|
|
90
|
+
dict_size: @dict_size,
|
|
91
|
+
uncompressed_size: @uncompressed_size)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Decode the .lzma stream
|
|
95
|
+
#
|
|
96
|
+
# @param output [IO, nil] Optional output stream
|
|
97
|
+
# @return [String, Integer] Decompressed data or bytes written
|
|
98
|
+
def decode_stream(output = nil)
|
|
99
|
+
# .lzma format allows EOPM even when uncompressed size is known
|
|
100
|
+
# Reference: alone_decoder.c:127 (LZMA_LZMA1EXT_ALLOW_EOPM)
|
|
101
|
+
@decoder.decode_stream(output, check_rc_finished: false)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
# Parse .lzma format header
|
|
107
|
+
#
|
|
108
|
+
# Format (from alone_decoder.c):
|
|
109
|
+
# - Properties (1 byte): lc/lp/pb encoded
|
|
110
|
+
# - Dictionary size (4 bytes, little-endian)
|
|
111
|
+
# - Uncompressed size (8 bytes, little-endian, UINT64_MAX = unknown)
|
|
112
|
+
#
|
|
113
|
+
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/alone_decoder.c
|
|
114
|
+
#
|
|
115
|
+
# @return [void]
|
|
116
|
+
# @raise [RuntimeError] If header is invalid
|
|
117
|
+
def parse_header
|
|
118
|
+
# Step 1: Parse properties byte (SEQ_PROPERTIES)
|
|
119
|
+
# Reference: alone_decoder.c:64-68
|
|
120
|
+
props = @input.getbyte
|
|
121
|
+
raise Omnizip::DecompressionError, "Invalid .lzma header: missing properties byte" if props.nil?
|
|
122
|
+
|
|
123
|
+
# Use XZ Utils property byte parsing
|
|
124
|
+
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/lzma/lzma_decoder.c:1216-1228
|
|
125
|
+
if props > MAX_PROPERTY_BYTE
|
|
126
|
+
raise Omnizip::DecompressionError, "Invalid .lzma header: properties byte #{props} exceeds maximum #{MAX_PROPERTY_BYTE}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Parse lc, lp, pb from properties byte
|
|
130
|
+
# Formula: pb = props / (9 * 5); lp = (props % 45) / 9; lc = (props % 45) % 9
|
|
131
|
+
@pb = props / (9 * 5)
|
|
132
|
+
remainder = props - (@pb * 9 * 5)
|
|
133
|
+
@lp = remainder / 9
|
|
134
|
+
@lc = remainder - (@lp * 9)
|
|
135
|
+
|
|
136
|
+
# Validate lc + lp <= 4 (LZMA_LCLP_MAX)
|
|
137
|
+
# Reference: lzma_decoder.c:1227
|
|
138
|
+
if @lc + @lp > 4
|
|
139
|
+
raise Omnizip::DecompressionError, "Invalid .lzma header: lc (#{@lc}) + lp (#{@lp}) exceeds maximum 4"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Step 2: Parse dictionary size (SEQ_DICTIONARY_SIZE)
|
|
143
|
+
# Reference: alone_decoder.c:71-96
|
|
144
|
+
@dict_size = 0
|
|
145
|
+
4.times do |i|
|
|
146
|
+
byte = @input.getbyte
|
|
147
|
+
raise Omnizip::DecompressionError, "Incomplete .lzma header: missing dictionary size byte" if byte.nil?
|
|
148
|
+
|
|
149
|
+
@dict_size |= (byte << (i * 8))
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Picky mode validation: only accept dictionary sizes that are
|
|
153
|
+
# 2^n or 2^n + 2^(n-1). This reduces false positives.
|
|
154
|
+
# Reference: alone_decoder.c:76-93
|
|
155
|
+
if @picky && @dict_size != 0xFFFFFFFF
|
|
156
|
+
# Check if dict_size is valid: 2^n or 2^n + 2^(n-1)
|
|
157
|
+
d = @dict_size - 1
|
|
158
|
+
d |= d >> 2
|
|
159
|
+
d |= d >> 3
|
|
160
|
+
d |= d >> 4
|
|
161
|
+
d |= d >> 8
|
|
162
|
+
d |= d >> 16
|
|
163
|
+
d += 1
|
|
164
|
+
|
|
165
|
+
if d != @dict_size
|
|
166
|
+
raise Omnizip::DecompressionError, "Invalid .lzma header: dictionary size #{@dict_size} is not 2^n or 2^n + 2^(n-1)"
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Step 3: Parse uncompressed size (SEQ_UNCOMPRESSED_SIZE)
|
|
171
|
+
# Reference: alone_decoder.c:102-120
|
|
172
|
+
@uncompressed_size = 0
|
|
173
|
+
8.times do |i|
|
|
174
|
+
byte = @input.getbyte
|
|
175
|
+
raise Omnizip::DecompressionError, "Incomplete .lzma header: missing uncompressed size byte" if byte.nil?
|
|
176
|
+
|
|
177
|
+
@uncompressed_size |= (byte << (i * 8))
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Picky mode validation: if uncompressed size is known (not UINT64_MAX),
|
|
181
|
+
# it must be less than 256 GiB
|
|
182
|
+
# Reference: alone_decoder.c:116-120
|
|
183
|
+
if @picky && @uncompressed_size != 0xFFFFFFFFFFFFFFFF &&
|
|
184
|
+
@uncompressed_size >= MAX_UNCOMPRESSED_SIZE
|
|
185
|
+
raise Omnizip::DecompressionError, "Invalid .lzma header: uncompressed size #{@uncompressed_size} exceeds maximum #{MAX_UNCOMPRESSED_SIZE}"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Note: XZ Utils uses UINT64_MAX (0xFFFFFFFFFFFFFFFF) for unknown size
|
|
189
|
+
# Our decoder treats this as "allow end-of-payload marker"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|