omnizip 0.4.7-aarch64-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,748 @@
|
|
|
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 Implementations
|
|
27
|
+
module XZUtils
|
|
28
|
+
module LZMA2
|
|
29
|
+
# XZ Utils LZMA2 encoder implementation.
|
|
30
|
+
#
|
|
31
|
+
# This is the original XzLZMA2Encoder moved from algorithms/lzma2/xz_lzma2_encoder.rb
|
|
32
|
+
# to the new namespace structure.
|
|
33
|
+
#
|
|
34
|
+
# Ported from XZ Utils liblzma/lzma2_encoder.c
|
|
35
|
+
#
|
|
36
|
+
# Constants
|
|
37
|
+
UINT32_MAX = 0xFFFFFFFF
|
|
38
|
+
REPS = 4
|
|
39
|
+
|
|
40
|
+
# XZ Utils LZMA2 encoder.
|
|
41
|
+
class Encoder < Base::LZMA2EncoderBase
|
|
42
|
+
include Omnizip::Algorithms::LZMA::Constants
|
|
43
|
+
|
|
44
|
+
# XZ Utils limits (from lzma2_encoder.h)
|
|
45
|
+
# Maximum UNCOMPRESSED size per chunk: 2MB
|
|
46
|
+
UNCOMPRESSED_MAX = 1 << 21 # 2,097,152 bytes
|
|
47
|
+
# Maximum COMPRESSED size per chunk: 64KB
|
|
48
|
+
COMPRESSED_MAX = 1 << 16 # 65,536 bytes
|
|
49
|
+
# Uncompressed chunks store their size in a 16-bit field,
|
|
50
|
+
# so each uncompressed chunk is also limited to 64KB.
|
|
51
|
+
UNCOMPRESSED_CHUNK_MAX = 1 << 16
|
|
52
|
+
|
|
53
|
+
# Initialize the encoder
|
|
54
|
+
# @param options [Hash] Encoding options
|
|
55
|
+
# @option options [Integer] :dict_size Dictionary size (default: 8MB)
|
|
56
|
+
# @option options [Integer] :lc Literal context bits (default: 3)
|
|
57
|
+
# @option options [Integer] :lp Literal position bits (default: 0)
|
|
58
|
+
# @option options [Integer] :pb Position bits (default: 2)
|
|
59
|
+
# @option options [Boolean] :standalone If true, write property byte at start (default: true)
|
|
60
|
+
def initialize(options = {})
|
|
61
|
+
dict_size = options.fetch(:dict_size, 8 * 1024 * 1024)
|
|
62
|
+
lc = options.fetch(:lc, 3)
|
|
63
|
+
lp = options.fetch(:lp, 0)
|
|
64
|
+
pb = options.fetch(:pb, 2)
|
|
65
|
+
standalone = options.fetch(:standalone, true)
|
|
66
|
+
|
|
67
|
+
super(
|
|
68
|
+
dict_size: dict_size,
|
|
69
|
+
lc: lc,
|
|
70
|
+
lp: lp,
|
|
71
|
+
pb: pb,
|
|
72
|
+
standalone: standalone
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Shared state across all chunks
|
|
76
|
+
@dictionary = Omnizip::Algorithms::LZMA::Dictionary.new(dict_size)
|
|
77
|
+
@state = Omnizip::Algorithms::LZMA::LZMAState.new(0)
|
|
78
|
+
@models = Omnizip::Algorithms::LZMA::XzProbabilityModels.new(lc,
|
|
79
|
+
lp, pb)
|
|
80
|
+
@match_finder = Omnizip::Algorithms::LZMA::MatchFinder.new(@dictionary)
|
|
81
|
+
@optimal = Omnizip::Algorithms::LZMA::OptimalEncoder.new(mode: :fast)
|
|
82
|
+
|
|
83
|
+
# Track previous byte for literal context
|
|
84
|
+
@prev_byte = 0
|
|
85
|
+
|
|
86
|
+
# CRITICAL: For XZ Utils compatibility, first chunk MUST reset the dictionary
|
|
87
|
+
# (matches XZ Utils behavior - see lzma2_encoder.c:334-336)
|
|
88
|
+
# need_dictionary_reset is set to true for the first compressed chunk
|
|
89
|
+
@need_state_reset = false
|
|
90
|
+
@need_dictionary_reset = true # Always reset dictionary for first chunk (XZ Utils compatibility)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def encode(input_data)
|
|
94
|
+
# CRITICAL: Reset match finder state for each encoding session
|
|
95
|
+
# This ensures hash table and hash chain start fresh for each Xz.create call
|
|
96
|
+
@match_finder.reset
|
|
97
|
+
|
|
98
|
+
output = StringIO.new
|
|
99
|
+
output.set_encoding(Encoding::BINARY)
|
|
100
|
+
|
|
101
|
+
# Write property byte if standalone mode (for .lz2 files)
|
|
102
|
+
# The property byte encodes dictionary size
|
|
103
|
+
if @standalone
|
|
104
|
+
output.putc(encode_dict_size(@dict_size))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
input = StringIO.new(input_data)
|
|
108
|
+
input.set_encoding(Encoding::BINARY)
|
|
109
|
+
|
|
110
|
+
# Process in chunks (UNCOMPRESSED_MAX = 2MB per chunk)
|
|
111
|
+
while !input.eof?
|
|
112
|
+
chunk_data = input.read(UNCOMPRESSED_MAX)
|
|
113
|
+
break if chunk_data.nil? || chunk_data.empty?
|
|
114
|
+
|
|
115
|
+
consume_chunk(chunk_data, output)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# End marker (0x00) is REQUIRED for all LZMA2 streams
|
|
119
|
+
# The @standalone flag only controls whether a property byte is written
|
|
120
|
+
# at the START (for raw LZMA2 format like .lz2), not the end marker.
|
|
121
|
+
# XZ format requires the end marker to properly terminate the LZMA2 stream.
|
|
122
|
+
output.write([0x00].pack("C"))
|
|
123
|
+
|
|
124
|
+
output.string
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Get implementation identifier.
|
|
128
|
+
#
|
|
129
|
+
# @return [Symbol] :xz_utils
|
|
130
|
+
def implementation_name
|
|
131
|
+
:xz_utils
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
|
|
136
|
+
# Encode chunk_data (<= UNCOMPRESSED_MAX bytes), emitting one or
|
|
137
|
+
# more LZMA2 chunks.
|
|
138
|
+
#
|
|
139
|
+
# Multiple chunks are required when the compressed output reaches
|
|
140
|
+
# COMPRESSED_MAX (the compressed size is stored in a 16-bit field)
|
|
141
|
+
# or when compression is not beneficial and the data must be
|
|
142
|
+
# stored as uncompressed chunks (each capped at 64KB).
|
|
143
|
+
def consume_chunk(chunk_data, output)
|
|
144
|
+
@match_finder.feed(chunk_data)
|
|
145
|
+
@match_finder.skip(@match_finder.buffer.bytesize)
|
|
146
|
+
|
|
147
|
+
offset = 0
|
|
148
|
+
while offset < chunk_data.bytesize
|
|
149
|
+
slice = chunk_data.byteslice(offset, chunk_data.bytesize - offset)
|
|
150
|
+
|
|
151
|
+
# A state reset must apply BEFORE encoding the chunk that
|
|
152
|
+
# announces it (0xC0/0xE0 control byte), so that the encoder's
|
|
153
|
+
# models match the decoder's from the chunk's first bit.
|
|
154
|
+
reset_lzma_state! if @need_state_reset || @need_dictionary_reset
|
|
155
|
+
|
|
156
|
+
compressed, consumed = try_compress(slice)
|
|
157
|
+
|
|
158
|
+
use_compressed =
|
|
159
|
+
consumed.positive? &&
|
|
160
|
+
compressed.bytesize <= COMPRESSED_MAX &&
|
|
161
|
+
compressed.bytesize < (consumed == slice.bytesize ? slice.bytesize : consumed)
|
|
162
|
+
|
|
163
|
+
if use_compressed
|
|
164
|
+
uncompressed = chunk_data.byteslice(offset, consumed)
|
|
165
|
+
emit_compressed_chunk(output, uncompressed, compressed)
|
|
166
|
+
|
|
167
|
+
offset += consumed
|
|
168
|
+
@need_state_reset = false
|
|
169
|
+
else
|
|
170
|
+
# Compression didn't help (or hit the cap immediately):
|
|
171
|
+
# store the whole slice as uncompressed chunks.
|
|
172
|
+
emit_uncompressed_chunks(output, slice)
|
|
173
|
+
|
|
174
|
+
offset += slice.bytesize
|
|
175
|
+
# After an uncompressed chunk the LZMA state is lost, so the
|
|
176
|
+
# next compressed chunk must reset it (XZ Utils behavior,
|
|
177
|
+
# lzma2_encoder.c).
|
|
178
|
+
@need_state_reset = true
|
|
179
|
+
end
|
|
180
|
+
@need_dictionary_reset = false
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Update prev_byte for next chunk
|
|
184
|
+
if chunk_data.bytesize.positive?
|
|
185
|
+
@prev_byte = chunk_data.getbyte(chunk_data.bytesize - 1)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Reset the LZMA state machine and probability models.
|
|
190
|
+
#
|
|
191
|
+
# MUST be called before encoding any chunk whose control byte
|
|
192
|
+
# announces a state reset (0xC0/0xE0), because the decoder will
|
|
193
|
+
# reinitialize its models on seeing that byte.
|
|
194
|
+
def reset_lzma_state!
|
|
195
|
+
@state = Omnizip::Algorithms::LZMA::LZMAState.new(0)
|
|
196
|
+
@models = Omnizip::Algorithms::LZMA::XzProbabilityModels.new(@lc,
|
|
197
|
+
@lp,
|
|
198
|
+
@pb)
|
|
199
|
+
@prev_byte = 0
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Emit a compressed LZMA2 chunk.
|
|
203
|
+
def emit_compressed_chunk(output, uncompressed, compressed)
|
|
204
|
+
# For compressed chunks, properties encode lc/lp/pb:
|
|
205
|
+
# (pb * 5 + lp) * 9 + lc
|
|
206
|
+
chunk_properties = (((@pb * 5) + @lp) * 9) + @lc
|
|
207
|
+
|
|
208
|
+
# A dictionary reset implies a state reset; props accompany
|
|
209
|
+
# every state reset (control 0xE0 or 0xC0).
|
|
210
|
+
state_reset = @need_state_reset || @need_dictionary_reset
|
|
211
|
+
|
|
212
|
+
chunk = Omnizip::Algorithms::LZMA2::LZMA2Chunk.new(
|
|
213
|
+
chunk_type: :compressed,
|
|
214
|
+
uncompressed_data: uncompressed,
|
|
215
|
+
compressed_data: compressed,
|
|
216
|
+
compressed_size: compressed.bytesize,
|
|
217
|
+
properties: chunk_properties,
|
|
218
|
+
need_dict_reset: @need_dictionary_reset,
|
|
219
|
+
need_state_reset: state_reset,
|
|
220
|
+
need_props: state_reset,
|
|
221
|
+
)
|
|
222
|
+
output.write(chunk.to_bytes)
|
|
223
|
+
|
|
224
|
+
# Update dictionary with the chunk data
|
|
225
|
+
@dictionary.append(uncompressed)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Emit uncompressed LZMA2 chunks for data (splitting at the
|
|
229
|
+
# 64KB per-chunk limit imposed by the 16-bit size field).
|
|
230
|
+
def emit_uncompressed_chunks(output, data)
|
|
231
|
+
offset = 0
|
|
232
|
+
while offset < data.bytesize
|
|
233
|
+
slice = data.byteslice(offset, UNCOMPRESSED_CHUNK_MAX)
|
|
234
|
+
chunk = Omnizip::Algorithms::LZMA2::LZMA2Chunk.new(
|
|
235
|
+
chunk_type: :uncompressed,
|
|
236
|
+
uncompressed_data: slice,
|
|
237
|
+
compressed_data: "",
|
|
238
|
+
need_dict_reset: @need_dictionary_reset,
|
|
239
|
+
need_state_reset: false,
|
|
240
|
+
need_props: false,
|
|
241
|
+
)
|
|
242
|
+
output.write(chunk.to_bytes)
|
|
243
|
+
@need_dictionary_reset = false
|
|
244
|
+
|
|
245
|
+
offset += slice.bytesize
|
|
246
|
+
@dictionary.append(slice)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Compress data starting at the current dictionary position.
|
|
251
|
+
#
|
|
252
|
+
# Stops early (returning fewer bytes than data.bytesize) when the
|
|
253
|
+
# compressed output is about to exceed COMPRESSED_MAX, leaving the
|
|
254
|
+
# range encoder at a clean item boundary.
|
|
255
|
+
#
|
|
256
|
+
# @return [Array<String, Integer>] compressed bytes and number of
|
|
257
|
+
# input bytes consumed
|
|
258
|
+
def try_compress(data)
|
|
259
|
+
# Create output buffer to capture compressed data
|
|
260
|
+
output_buffer = StringIO.new
|
|
261
|
+
output_buffer.set_encoding(Encoding::BINARY)
|
|
262
|
+
|
|
263
|
+
# Create range encoder (direct XZ Utils port)
|
|
264
|
+
encoder = Omnizip::Algorithms::LZMA::XzRangeEncoder.new(output_buffer)
|
|
265
|
+
|
|
266
|
+
# The whole chunk has already been fed to the match finder by
|
|
267
|
+
# consume_chunk; positions map from the dictionary position.
|
|
268
|
+
start_pos = @dictionary.buffer.bytesize
|
|
269
|
+
|
|
270
|
+
# Headroom for pending symbols plus the final flush
|
|
271
|
+
size_cap = COMPRESSED_MAX - 64
|
|
272
|
+
|
|
273
|
+
pos = 0
|
|
274
|
+
while pos < data.bytesize
|
|
275
|
+
# Drain before every item: a single worst-case match (long
|
|
276
|
+
# length + dist slot >= 14 with up to 30 footer bits) queues
|
|
277
|
+
# ~48 symbols, and RC_SYMBOLS_MAX is 53, so the queue MUST be
|
|
278
|
+
# empty when an item starts or `bit` raises "Symbol buffer
|
|
279
|
+
# overflow" (issue #26).
|
|
280
|
+
encode_queued_symbols(encoder, output_buffer)
|
|
281
|
+
|
|
282
|
+
# Enforce the 64KB compressed-chunk cap at an item boundary
|
|
283
|
+
break if pos.positive? && output_buffer.size >= size_cap
|
|
284
|
+
|
|
285
|
+
# Position in match finder's buffer for encoding
|
|
286
|
+
match_pos = start_pos + pos
|
|
287
|
+
|
|
288
|
+
# Get optimal encoding choice
|
|
289
|
+
# Note: find_optimal calls find_matches internally, no need to call twice
|
|
290
|
+
distance, length = @optimal.find_optimal(
|
|
291
|
+
match_pos,
|
|
292
|
+
@match_finder,
|
|
293
|
+
@state,
|
|
294
|
+
@state.reps,
|
|
295
|
+
@models,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# DEBUG: Trace encoding decisions
|
|
299
|
+
puts "[DEBUG] pos=#{pos} distance=#{distance} length=#{length} state=#{@state.value} reps=#{@state.reps.inspect}" if ENV["DEBUG"]
|
|
300
|
+
|
|
301
|
+
# Encode based on choice
|
|
302
|
+
# CRITICAL: Use UINT32_MAX to check for literal (not distance.zero?)
|
|
303
|
+
# because distance=0 means repeated match rep0, not literal!
|
|
304
|
+
if distance == UINT32_MAX || length == 1
|
|
305
|
+
# Encode literal. The position argument must be GLOBAL
|
|
306
|
+
# (dictionary position), because the decoder keeps counting
|
|
307
|
+
# bytes across chunk boundaries for pos_state.
|
|
308
|
+
encode_literal(data.getbyte(pos), encoder, match_pos)
|
|
309
|
+
pos += 1
|
|
310
|
+
elsif distance < REPS
|
|
311
|
+
# Encode repeated match (distance is 0-3 for rep0-rep3)
|
|
312
|
+
encode_repeated_match(distance, length, encoder, match_pos,
|
|
313
|
+
match_pos)
|
|
314
|
+
pos += length
|
|
315
|
+
else
|
|
316
|
+
# Encode normal match (distance is actual_distance + REPS)
|
|
317
|
+
actual_distance = distance - REPS
|
|
318
|
+
encode_match(actual_distance, length, encoder, match_pos,
|
|
319
|
+
match_pos, data)
|
|
320
|
+
pos += length
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Flush encoder to write remaining bytes
|
|
325
|
+
# IMPORTANT: Encode all pending symbols FIRST, before queue_flush
|
|
326
|
+
encode_queued_symbols(encoder, output_buffer)
|
|
327
|
+
|
|
328
|
+
# Now flush the encoder (adds 5 RC_FLUSH symbols)
|
|
329
|
+
encoder.queue_flush
|
|
330
|
+
|
|
331
|
+
# Encode the flush symbols
|
|
332
|
+
# This will write additional bytes to output_buffer
|
|
333
|
+
encode_queued_symbols(encoder, output_buffer)
|
|
334
|
+
|
|
335
|
+
[output_buffer.string, pos]
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Encode queued symbols to output
|
|
339
|
+
def encode_queued_symbols(encoder, output)
|
|
340
|
+
return if encoder.none?
|
|
341
|
+
|
|
342
|
+
# Reused scratch buffer: allocating a fresh 10 KB String
|
|
343
|
+
# per drain showed up as half the encoder's runtime via
|
|
344
|
+
# GC (String#* in profiles).
|
|
345
|
+
@symbol_buffer ||= "\0".b * 10_000
|
|
346
|
+
temp_buffer = @symbol_buffer
|
|
347
|
+
out_pos = Omnizip::Algorithms::LZMA::IntRef.new(0)
|
|
348
|
+
|
|
349
|
+
# Track size before encoding
|
|
350
|
+
size_before = output.size
|
|
351
|
+
|
|
352
|
+
# Encode symbols to buffer
|
|
353
|
+
encoder.encode_symbols(temp_buffer, out_pos, 10000)
|
|
354
|
+
|
|
355
|
+
# Write to output stream
|
|
356
|
+
if out_pos.value.positive?
|
|
357
|
+
# Ruby's [] operator has a bug with null bytes that can return extra bytes
|
|
358
|
+
# See: https://bugs.ruby-lang.org/issues/15985
|
|
359
|
+
output.write(temp_buffer.byteslice(0, out_pos.value))
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Return the number of bytes written
|
|
363
|
+
output.size - size_before
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Encode literal byte
|
|
367
|
+
# pos is the GLOBAL position (dictionary position) because the
|
|
368
|
+
# decoder counts bytes continuously across chunks.
|
|
369
|
+
def encode_literal(symbol, encoder, pos)
|
|
370
|
+
pos_state = pos & ((1 << @pb) - 1)
|
|
371
|
+
|
|
372
|
+
# Encode is_match bit (0 for literal) - uses OLD state value
|
|
373
|
+
prob_is_match = @models.is_match[@state.value][pos_state]
|
|
374
|
+
encoder.queue_bit(prob_is_match, 0)
|
|
375
|
+
|
|
376
|
+
# Get literal subcoder flat index (uses OLD state value via @prev_byte)
|
|
377
|
+
# This is the base offset into the flat literal array
|
|
378
|
+
literal_offset = get_literal_state(pos, @prev_byte)
|
|
379
|
+
|
|
380
|
+
# CRITICAL: Check encoding path BEFORE updating state (XZ Utils order)
|
|
381
|
+
# The is_literal_state check happens on the current state
|
|
382
|
+
use_matched = @state.use_matched_literal?
|
|
383
|
+
|
|
384
|
+
# Now update state (this is the update_literal() call in XZ)
|
|
385
|
+
@state.update_literal!
|
|
386
|
+
|
|
387
|
+
if use_matched
|
|
388
|
+
# Matched literal (compare with match byte at rep0)
|
|
389
|
+
# XZ Utils: mf->buffer[mf->read_pos - coder->reps[0] - 1 - mf->read_ahead]
|
|
390
|
+
# We don't use read_ahead, so it's 0
|
|
391
|
+
match_byte_pos = pos - @state.reps[0] - 1
|
|
392
|
+
match_byte = @match_finder.buffer.getbyte(match_byte_pos) if match_byte_pos >= 0 && match_byte_pos < @match_finder.buffer.bytesize
|
|
393
|
+
|
|
394
|
+
# If match_byte is nil (shouldn't happen in normal operation),
|
|
395
|
+
# fall back to normal literal encoding
|
|
396
|
+
if match_byte.nil?
|
|
397
|
+
encode_normal_literal(literal_offset, symbol, encoder)
|
|
398
|
+
else
|
|
399
|
+
encode_matched_literal(literal_offset, match_byte, symbol,
|
|
400
|
+
encoder)
|
|
401
|
+
end
|
|
402
|
+
else
|
|
403
|
+
# Normal literal (8-bit tree)
|
|
404
|
+
encode_normal_literal(literal_offset, symbol, encoder)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# Update prev_byte
|
|
408
|
+
@prev_byte = symbol
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# Encode normal match
|
|
412
|
+
def encode_match(distance, length, encoder, pos, match_pos,
|
|
413
|
+
_input_data)
|
|
414
|
+
pos_state = pos & ((1 << @pb) - 1)
|
|
415
|
+
|
|
416
|
+
# Encode is_match bit (1 for match) - uses OLD state value
|
|
417
|
+
prob_is_match = @models.is_match[@state.value][pos_state]
|
|
418
|
+
encoder.queue_bit(prob_is_match, 1)
|
|
419
|
+
|
|
420
|
+
# Encode is_rep bit (0 for normal match) - uses OLD state value
|
|
421
|
+
prob_is_rep = @models.is_rep[@state.value]
|
|
422
|
+
encoder.queue_bit(prob_is_rep, 0)
|
|
423
|
+
|
|
424
|
+
# CRITICAL: Update state BEFORE encoding length/distance (XZ Utils order)
|
|
425
|
+
# This also updates reps. Reps are stored 0-based (distance - 1),
|
|
426
|
+
# matching XZ Utils and the decoder's rep0 convention.
|
|
427
|
+
@state.update_match!(distance - 1)
|
|
428
|
+
|
|
429
|
+
# Encode length - uses NEW state value
|
|
430
|
+
encode_match_length(length, pos_state, encoder)
|
|
431
|
+
|
|
432
|
+
# Encode distance - uses NEW state value
|
|
433
|
+
encode_distance(distance, length, encoder)
|
|
434
|
+
|
|
435
|
+
# Update prev_byte (last byte of match)
|
|
436
|
+
# Read from match finder buffer: match_pos - distance + length - 1
|
|
437
|
+
last_byte_pos = match_pos - distance + length - 1
|
|
438
|
+
@prev_byte = @match_finder.buffer.getbyte(last_byte_pos) if last_byte_pos >= 0 && last_byte_pos < @match_finder.buffer.bytesize
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Encode repeated match (using rep0-rep3)
|
|
442
|
+
# Ported from XZ Utils rep_match function
|
|
443
|
+
def encode_repeated_match(rep, length, encoder, pos, match_pos)
|
|
444
|
+
pos_state = pos & ((1 << @pb) - 1)
|
|
445
|
+
|
|
446
|
+
# Encode is_match bit (1 for match) - uses OLD state value
|
|
447
|
+
prob_is_match = @models.is_match[@state.value][pos_state]
|
|
448
|
+
encoder.queue_bit(prob_is_match, 1)
|
|
449
|
+
|
|
450
|
+
# Encode is_rep bit (1 for repeated match) - uses OLD state value
|
|
451
|
+
prob_is_rep = @models.is_rep[@state.value]
|
|
452
|
+
encoder.queue_bit(prob_is_rep, 1)
|
|
453
|
+
|
|
454
|
+
prob_is_rep0 = @models.is_rep0[@state.value]
|
|
455
|
+
if rep.zero?
|
|
456
|
+
# rep0 (shortest distance)
|
|
457
|
+
encoder.queue_bit(prob_is_rep0, 0)
|
|
458
|
+
|
|
459
|
+
prob_is_rep0_long = @models.is_rep0_long[@state.value][pos_state]
|
|
460
|
+
encoder.queue_bit(prob_is_rep0_long, length == 1 ? 0 : 1)
|
|
461
|
+
else
|
|
462
|
+
# rep1, rep2, or rep3
|
|
463
|
+
encoder.queue_bit(prob_is_rep0, 1)
|
|
464
|
+
|
|
465
|
+
prob_is_rep1 = @models.is_rep1[@state.value]
|
|
466
|
+
if rep == 1
|
|
467
|
+
# rep1
|
|
468
|
+
encoder.queue_bit(prob_is_rep1, 0)
|
|
469
|
+
else
|
|
470
|
+
# rep2 or rep3
|
|
471
|
+
encoder.queue_bit(prob_is_rep1, 1)
|
|
472
|
+
|
|
473
|
+
prob_is_rep2 = @models.is_rep2[@state.value]
|
|
474
|
+
encoder.queue_bit(prob_is_rep2, rep - 2)
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
# XZ Utils rep_match: capture the selected distance BEFORE
|
|
478
|
+
# rotating, then shift reps down (lzma_encoder.c):
|
|
479
|
+
# distance = reps[rep];
|
|
480
|
+
# for (i = rep; i > 0; --i) reps[i] = reps[i-1];
|
|
481
|
+
# reps[0] = distance;
|
|
482
|
+
distance = @state.reps[rep]
|
|
483
|
+
rep.downto(1) { |i| @state.reps[i] = @state.reps[i - 1] }
|
|
484
|
+
@state.reps[0] = distance
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# Update state based on match length
|
|
488
|
+
if length == 1
|
|
489
|
+
@state.update_short_rep!
|
|
490
|
+
else
|
|
491
|
+
# Encode length using the REP length coder (rep matches have
|
|
492
|
+
# their own length models, decoder's @rep_length_coder)
|
|
493
|
+
encode_match_length(length, pos_state, encoder,
|
|
494
|
+
@models.rep_len_encoder)
|
|
495
|
+
@state.update_long_rep!
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# Update prev_byte (last byte of the match region in the stream)
|
|
499
|
+
last_byte_pos = match_pos + length - 1
|
|
500
|
+
@prev_byte = @match_finder.buffer.getbyte(last_byte_pos) if last_byte_pos >= 0 && last_byte_pos < @match_finder.buffer.bytesize
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
# Get literal subcoder flat index
|
|
504
|
+
# Ported from XZ Utils literal_subcoder macro in lzma_common.h:
|
|
505
|
+
# #define literal_subcoder(probs, lc, literal_mask, pos, prev_byte) \
|
|
506
|
+
# ((probs) + UINT32_C(3) * \
|
|
507
|
+
# (((((pos) << 8) + (prev_byte)) & (literal_mask)) << (lc)))
|
|
508
|
+
# where literal_mask = (0x100 << lp) - (0x100 >> lc)
|
|
509
|
+
#
|
|
510
|
+
# The factor of 3 gives each context a 0x300-sized subcoder and
|
|
511
|
+
# MUST match the decoder's base_offset = 3 * (lit_state << lc).
|
|
512
|
+
#
|
|
513
|
+
# Returns the flat index into the literal probability array.
|
|
514
|
+
def get_literal_state(pos, prev_byte)
|
|
515
|
+
literal_mask = (0x100 << @lp) - (0x100 >> @lc)
|
|
516
|
+
3 * ((((pos << 8) + prev_byte) & literal_mask) << @lc)
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# Get byte from dictionary at distance back
|
|
520
|
+
def get_dictionary_byte(distance)
|
|
521
|
+
if distance.positive? &&
|
|
522
|
+
distance <= @dictionary.buffer.bytesize
|
|
523
|
+
@dictionary.get_byte(distance)
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# Encode normal literal (8-bit tree)
|
|
528
|
+
# Ported from XZ Utils rc_bittree() for normal literals
|
|
529
|
+
# @param literal_offset [Integer] Base offset into flat literal array
|
|
530
|
+
# @param symbol [Integer] The literal byte to encode (0-255)
|
|
531
|
+
# @param encoder [XZBufferedRangeEncoder] The range encoder
|
|
532
|
+
def encode_normal_literal(literal_offset, symbol, encoder)
|
|
533
|
+
context = 1
|
|
534
|
+
8.downto(1) do |i|
|
|
535
|
+
bit = (symbol >> (i - 1)) & 1
|
|
536
|
+
encoder.queue_bit(@models.literal[literal_offset + context], bit)
|
|
537
|
+
context = (context << 1) | bit
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
# Encode matched literal (compare with match byte)
|
|
542
|
+
# Ported from XZ Utils literal_matched() in lzma_encoder.c
|
|
543
|
+
# @param literal_offset [Integer] Base offset into flat literal array
|
|
544
|
+
# @param match_byte [Integer] The match byte to compare against
|
|
545
|
+
# @param symbol [Integer] The literal byte to encode (0-255)
|
|
546
|
+
# @param encoder [XZBufferedRangeEncoder] The range encoder
|
|
547
|
+
def encode_matched_literal(literal_offset, match_byte, symbol,
|
|
548
|
+
encoder)
|
|
549
|
+
offset = 0x100
|
|
550
|
+
symbol += 0x100 # Start symbol at 256 (XZ Utils algorithm)
|
|
551
|
+
|
|
552
|
+
# Loop until symbol reaches 0x10000 (65536)
|
|
553
|
+
while symbol < 0x10000
|
|
554
|
+
match_byte <<= 1
|
|
555
|
+
match_bit = match_byte & offset
|
|
556
|
+
subcoder_index = offset + match_bit + (symbol >> 8)
|
|
557
|
+
bit = (symbol >> 7) & 1
|
|
558
|
+
|
|
559
|
+
encoder.queue_bit(@models.literal[literal_offset + subcoder_index],
|
|
560
|
+
bit)
|
|
561
|
+
|
|
562
|
+
symbol <<= 1
|
|
563
|
+
offset &= ~(match_byte ^ symbol)
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# Encode match length
|
|
568
|
+
# len_encoder is the length model set to use: match_len_encoder for
|
|
569
|
+
# normal matches, rep_len_encoder for repeated matches.
|
|
570
|
+
def encode_match_length(length, pos_state, encoder,
|
|
571
|
+
len_encoder = @models.match_len_encoder)
|
|
572
|
+
len = length - MATCH_LEN_MIN
|
|
573
|
+
|
|
574
|
+
if len < LEN_LOW_SYMBOLS
|
|
575
|
+
# Low: 0-7
|
|
576
|
+
encoder.queue_bit(len_encoder.choice, 0)
|
|
577
|
+
encode_bittree(
|
|
578
|
+
len_encoder.low[pos_state],
|
|
579
|
+
NUM_LEN_LOW_BITS,
|
|
580
|
+
len,
|
|
581
|
+
encoder,
|
|
582
|
+
)
|
|
583
|
+
elsif len < LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS
|
|
584
|
+
# Mid: 8-15
|
|
585
|
+
encoder.queue_bit(len_encoder.choice, 1)
|
|
586
|
+
encoder.queue_bit(len_encoder.choice2, 0)
|
|
587
|
+
encode_bittree(
|
|
588
|
+
len_encoder.mid[pos_state],
|
|
589
|
+
NUM_LEN_MID_BITS,
|
|
590
|
+
len - LEN_LOW_SYMBOLS,
|
|
591
|
+
encoder,
|
|
592
|
+
)
|
|
593
|
+
else
|
|
594
|
+
# High: 16-271
|
|
595
|
+
encoder.queue_bit(len_encoder.choice, 1)
|
|
596
|
+
encoder.queue_bit(len_encoder.choice2, 1)
|
|
597
|
+
high_len = len - LEN_LOW_SYMBOLS - LEN_MID_SYMBOLS
|
|
598
|
+
encode_bittree(
|
|
599
|
+
len_encoder.high,
|
|
600
|
+
NUM_LEN_HIGH_BITS,
|
|
601
|
+
high_len,
|
|
602
|
+
encoder,
|
|
603
|
+
)
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# Encode distance using slot encoding
|
|
608
|
+
def encode_distance(distance, length, encoder)
|
|
609
|
+
# get_dist_slot expects a 0-based distance (xz fastpos.h:
|
|
610
|
+
# dist 4 -> slot 4, dist 5 -> slot 4). The decoder computes
|
|
611
|
+
# rep0 = base + footer the same 0-based way.
|
|
612
|
+
dist0 = distance - 1
|
|
613
|
+
dist_slot = get_dist_slot(dist0)
|
|
614
|
+
len_state = get_len_to_pos_state(length)
|
|
615
|
+
|
|
616
|
+
# Encode distance slot
|
|
617
|
+
# @dist_slot is organized as [len_to_pos_state][dist_slot]
|
|
618
|
+
encode_bittree(
|
|
619
|
+
@models.dist_slot[len_state],
|
|
620
|
+
NUM_DIST_SLOT_BITS,
|
|
621
|
+
dist_slot,
|
|
622
|
+
encoder,
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
# Encode distance footer
|
|
626
|
+
if dist_slot >= START_POS_MODEL_INDEX
|
|
627
|
+
footer_bits = (dist_slot >> 1) - 1
|
|
628
|
+
base = (2 | (dist_slot & 1)) << footer_bits
|
|
629
|
+
dist_reduced = dist0 - base
|
|
630
|
+
|
|
631
|
+
if dist_slot < END_POS_MODEL_INDEX
|
|
632
|
+
# Use probability models
|
|
633
|
+
# XZ Utils: rc_bittree_reverse(&coder->rc, coder->dist_special + base - dist_slot - 1, ...)
|
|
634
|
+
encode_bittree_reverse(
|
|
635
|
+
@models.dist_special,
|
|
636
|
+
dist_reduced,
|
|
637
|
+
footer_bits,
|
|
638
|
+
base - dist_slot - 1,
|
|
639
|
+
encoder,
|
|
640
|
+
)
|
|
641
|
+
else
|
|
642
|
+
# Direct bits + alignment
|
|
643
|
+
direct_bits = footer_bits - DIST_ALIGN_BITS
|
|
644
|
+
encoder.queue_direct_bits(
|
|
645
|
+
dist_reduced >> DIST_ALIGN_BITS,
|
|
646
|
+
direct_bits,
|
|
647
|
+
)
|
|
648
|
+
align_mask = (1 << DIST_ALIGN_BITS) - 1
|
|
649
|
+
encode_bittree_reverse(
|
|
650
|
+
@models.dist_align,
|
|
651
|
+
dist_reduced & align_mask,
|
|
652
|
+
DIST_ALIGN_BITS,
|
|
653
|
+
0,
|
|
654
|
+
encoder,
|
|
655
|
+
)
|
|
656
|
+
end
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# Encode bittree (MSB first)
|
|
661
|
+
def encode_bittree(probs, num_bits, value, encoder)
|
|
662
|
+
context = 1
|
|
663
|
+
num_bits.downto(1) do |i|
|
|
664
|
+
bit = (value >> (i - 1)) & 1
|
|
665
|
+
encoder.queue_bit(probs[context], bit)
|
|
666
|
+
context = (context << 1) | bit
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
# Encode bittree in reverse (LSB first)
|
|
671
|
+
def encode_bittree_reverse(probs, value, num_bits, offset, encoder)
|
|
672
|
+
context = 1
|
|
673
|
+
num_bits.times do |i|
|
|
674
|
+
bit = (value >> i) & 1
|
|
675
|
+
encoder.queue_bit(probs[offset + context], bit)
|
|
676
|
+
context = (context << 1) | bit
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
# Get distance slot for distance
|
|
681
|
+
def get_dist_slot(distance)
|
|
682
|
+
if distance < NUM_FULL_DISTANCES
|
|
683
|
+
distance < 4 ? distance : fast_pos_small(distance)
|
|
684
|
+
else
|
|
685
|
+
fast_pos_large(distance)
|
|
686
|
+
end
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
# Fast position calculation for small distances
|
|
690
|
+
def fast_pos_small(distance)
|
|
691
|
+
slot = 0
|
|
692
|
+
dist = distance
|
|
693
|
+
while dist > 3
|
|
694
|
+
dist >>= 1
|
|
695
|
+
slot += 2
|
|
696
|
+
end
|
|
697
|
+
slot + dist
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
# Fast position calculation for large distances
|
|
701
|
+
def fast_pos_large(distance)
|
|
702
|
+
# Find highest bit position
|
|
703
|
+
n = 31
|
|
704
|
+
while n >= 0
|
|
705
|
+
break if (distance >> n) != 0
|
|
706
|
+
|
|
707
|
+
n -= 1
|
|
708
|
+
end
|
|
709
|
+
# slot = 2 * n + high_bit
|
|
710
|
+
((n << 1) + ((distance >> (n - 1)) & 1))
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
# Map length to position state
|
|
714
|
+
def get_len_to_pos_state(length)
|
|
715
|
+
len = length - MATCH_LEN_MIN
|
|
716
|
+
len < NUM_LEN_TO_POS_STATES ? len : NUM_LEN_TO_POS_STATES - 1
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
# Encode dictionary size to LZMA2 property byte
|
|
720
|
+
# @param dict_size [Integer] Dictionary size
|
|
721
|
+
# @return [Integer] Property byte (0-40)
|
|
722
|
+
def encode_dict_size(dict_size)
|
|
723
|
+
# Clamp to valid range
|
|
724
|
+
d = [dict_size, Omnizip::Algorithms::LZMA2Const::DICT_SIZE_MIN].max
|
|
725
|
+
|
|
726
|
+
# Calculate log2 of dict_size
|
|
727
|
+
log2_size = 0
|
|
728
|
+
temp = d
|
|
729
|
+
while temp > 1
|
|
730
|
+
log2_size += 1
|
|
731
|
+
temp >>= 1
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
# Encoding formula for power-of-2 sizes:
|
|
735
|
+
# d = 2 * (log2_size - 12)
|
|
736
|
+
if d == (1 << log2_size)
|
|
737
|
+
# Exact power of 2
|
|
738
|
+
[(log2_size - 12) * 2, 40].min
|
|
739
|
+
else
|
|
740
|
+
# Between 2^n and 2^n + 2^(n-1), use odd encoding
|
|
741
|
+
[((log2_size - 12) * 2) + 1, 40].min
|
|
742
|
+
end
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
end
|
|
746
|
+
end
|
|
747
|
+
end
|
|
748
|
+
end
|