omnizip 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +754 -0
- data/COPYING +502 -0
- data/Gemfile +17 -0
- data/LICENSE +12 -0
- data/README.adoc +1045 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +91 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +271 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +185 -0
- data/docs/getting-started/your-first-archive.adoc +218 -0
- data/docs/guides/advanced-features/encryption.adoc +300 -0
- data/docs/guides/advanced-features/index.adoc +49 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
- data/docs/guides/advanced-features/streaming.adoc +212 -0
- data/docs/guides/archive-formats/gzip-format.adoc +107 -0
- data/docs/guides/archive-formats/index.adoc +130 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
- data/docs/guides/compression-algorithms/deflate.adoc +319 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +398 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
- data/docs/guides/creating-archives.adoc +354 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +218 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +188 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/troubleshooting/index.adoc +208 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +130 -0
- data/lib/omnizip/algorithm_registry.rb +86 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
- data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
- data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
- data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
- data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
- data/lib/omnizip/algorithms/bzip2.rb +130 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
- data/lib/omnizip/algorithms/deflate.rb +128 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
- data/lib/omnizip/algorithms/deflate64.rb +109 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
- data/lib/omnizip/algorithms/lzma.rb +238 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +141 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +116 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +82 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
- data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
- data/lib/omnizip/algorithms/zstandard.rb +138 -0
- data/lib/omnizip/buffer/memory_archive.rb +251 -0
- data/lib/omnizip/buffer/memory_extractor.rb +224 -0
- data/lib/omnizip/buffer.rb +176 -0
- data/lib/omnizip/checksum_registry.rb +114 -0
- data/lib/omnizip/checksums/crc32.rb +100 -0
- data/lib/omnizip/checksums/crc64.rb +101 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/verifier.rb +131 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +120 -0
- data/lib/omnizip/chunked.rb +129 -0
- data/lib/omnizip/cli/output_formatter.rb +104 -0
- data/lib/omnizip/cli.rb +572 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +427 -0
- data/lib/omnizip/commands/archive_extract_command.rb +272 -0
- data/lib/omnizip/commands/archive_list_command.rb +218 -0
- data/lib/omnizip/commands/archive_repair_command.rb +131 -0
- data/lib/omnizip/commands/archive_verify_command.rb +117 -0
- data/lib/omnizip/commands/compress_command.rb +117 -0
- data/lib/omnizip/commands/decompress_command.rb +120 -0
- data/lib/omnizip/commands/list_command.rb +53 -0
- data/lib/omnizip/commands/metadata_command.rb +153 -0
- data/lib/omnizip/commands/parity_create_command.rb +122 -0
- data/lib/omnizip/commands/parity_repair_command.rb +122 -0
- data/lib/omnizip/commands/parity_verify_command.rb +124 -0
- data/lib/omnizip/commands/profile_list_command.rb +56 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/convenience.rb +359 -0
- data/lib/omnizip/converter/conversion_registry.rb +49 -0
- data/lib/omnizip/converter/conversion_strategy.rb +121 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
- data/lib/omnizip/converter.rb +105 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/error.rb +106 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +63 -0
- data/lib/omnizip/extraction/filter_chain.rb +177 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +240 -0
- data/lib/omnizip/extraction.rb +111 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +113 -0
- data/lib/omnizip/filter.rb +139 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +166 -0
- data/lib/omnizip/filters/bcj.rb +279 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
- data/lib/omnizip/filters/bcj2.rb +99 -0
- data/lib/omnizip/filters/bcj_arm.rb +176 -0
- data/lib/omnizip/filters/bcj_arm64.rb +244 -0
- data/lib/omnizip/filters/bcj_ia64.rb +196 -0
- data/lib/omnizip/filters/bcj_ppc.rb +190 -0
- data/lib/omnizip/filters/bcj_sparc.rb +176 -0
- data/lib/omnizip/filters/bcj_x86.rb +193 -0
- data/lib/omnizip/filters/delta.rb +196 -0
- data/lib/omnizip/filters/filter_base.rb +72 -0
- data/lib/omnizip/filters/registry.rb +123 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/format_detector.rb +162 -0
- data/lib/omnizip/format_registry.rb +59 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +172 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +385 -0
- data/lib/omnizip/formats/cpio/reader.rb +196 -0
- data/lib/omnizip/formats/cpio/writer.rb +234 -0
- data/lib/omnizip/formats/cpio.rb +140 -0
- data/lib/omnizip/formats/format_spec_loader.rb +230 -0
- data/lib/omnizip/formats/gzip.rb +238 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
- data/lib/omnizip/formats/iso/directory_record.rb +152 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +197 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +530 -0
- data/lib/omnizip/formats/iso.rb +140 -0
- data/lib/omnizip/formats/lzip.rb +175 -0
- data/lib/omnizip/formats/lzma_alone.rb +171 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
- data/lib/omnizip/formats/rar/block_parser.rb +243 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +82 -0
- data/lib/omnizip/formats/rar/decompressor.rb +238 -0
- data/lib/omnizip/formats/rar/external_writer.rb +312 -0
- data/lib/omnizip/formats/rar/header.rb +192 -0
- data/lib/omnizip/formats/rar/license_validator.rb +109 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
- data/lib/omnizip/formats/rar/reader.rb +366 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
- data/lib/omnizip/formats/rar/writer.rb +431 -0
- data/lib/omnizip/formats/rar.rb +205 -0
- data/lib/omnizip/formats/rar3/compressor.rb +73 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar3/reader.rb +386 -0
- data/lib/omnizip/formats/rar3/writer.rb +219 -0
- data/lib/omnizip/formats/rar5/compressor.rb +73 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar5/reader.rb +342 -0
- data/lib/omnizip/formats/rar5/writer.rb +214 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
- data/lib/omnizip/formats/seven_zip/header.rb +106 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
- data/lib/omnizip/formats/seven_zip.rb +93 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +94 -0
- data/lib/omnizip/formats/tar/header.rb +168 -0
- data/lib/omnizip/formats/tar/reader.rb +121 -0
- data/lib/omnizip/formats/tar/writer.rb +216 -0
- data/lib/omnizip/formats/tar.rb +84 -0
- data/lib/omnizip/formats/xz/reader.rb +116 -0
- data/lib/omnizip/formats/xz.rb +237 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
- data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
- data/lib/omnizip/formats/zip/reader.rb +250 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +375 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
- data/lib/omnizip/io/buffered_input.rb +146 -0
- data/lib/omnizip/io/buffered_output.rb +105 -0
- data/lib/omnizip/io/stream_manager.rb +115 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +124 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +146 -0
- data/lib/omnizip/metadata/metadata_editor.rb +171 -0
- data/lib/omnizip/metadata/metadata_registry.rb +64 -0
- data/lib/omnizip/metadata/metadata_validator.rb +99 -0
- data/lib/omnizip/metadata.rb +57 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +73 -0
- data/lib/omnizip/models/compression_options.rb +71 -0
- data/lib/omnizip/models/conversion_options.rb +87 -0
- data/lib/omnizip/models/conversion_result.rb +135 -0
- data/lib/omnizip/models/eta_result.rb +46 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +144 -0
- data/lib/omnizip/models/filter_config.rb +183 -0
- data/lib/omnizip/models/match_result.rb +124 -0
- data/lib/omnizip/models/optimization_suggestion.rb +91 -0
- data/lib/omnizip/models/parallel_options.rb +104 -0
- data/lib/omnizip/models/performance_result.rb +79 -0
- data/lib/omnizip/models/profile_report.rb +82 -0
- data/lib/omnizip/models/progress_options.rb +38 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +81 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +203 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
- data/lib/omnizip/parallel/worker_pool.rb +223 -0
- data/lib/omnizip/parallel.rb +149 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +73 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
- data/lib/omnizip/parity/models/main_packet.rb +128 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +109 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
- data/lib/omnizip/parity/par2_creator.rb +531 -0
- data/lib/omnizip/parity/par2_repairer.rb +407 -0
- data/lib/omnizip/parity/par2_verifier.rb +364 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
- data/lib/omnizip/parity.rb +186 -0
- data/lib/omnizip/password/encryption_registry.rb +65 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
- data/lib/omnizip/password.rb +87 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
- data/lib/omnizip/pipe.rb +121 -0
- data/lib/omnizip/platform/ntfs_streams.rb +201 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +39 -0
- data/lib/omnizip/profile/balanced_profile.rb +33 -0
- data/lib/omnizip/profile/binary_profile.rb +36 -0
- data/lib/omnizip/profile/compression_profile.rb +158 -0
- data/lib/omnizip/profile/custom_profile.rb +157 -0
- data/lib/omnizip/profile/fast_profile.rb +33 -0
- data/lib/omnizip/profile/maximum_profile.rb +33 -0
- data/lib/omnizip/profile/profile_detector.rb +110 -0
- data/lib/omnizip/profile/profile_registry.rb +161 -0
- data/lib/omnizip/profile/text_profile.rb +36 -0
- data/lib/omnizip/profile.rb +190 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +169 -0
- data/lib/omnizip/profiler.rb +204 -0
- data/lib/omnizip/progress/callback_reporter.rb +36 -0
- data/lib/omnizip/progress/console_reporter.rb +62 -0
- data/lib/omnizip/progress/log_reporter.rb +91 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +190 -0
- data/lib/omnizip/progress/silent_reporter.rb +24 -0
- data/lib/omnizip/progress.rb +127 -0
- data/lib/omnizip/rubyzip_compat.rb +63 -0
- data/lib/omnizip/temp/safe_extract.rb +168 -0
- data/lib/omnizip/temp/temp_file.rb +124 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +181 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +156 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +273 -0
- data/lib/omnizip/zip/output_stream.rb +324 -0
- data/lib/omnizip.rb +156 -0
- data/readme-docs/advanced-features.adoc +515 -0
- data/readme-docs/api-usage.adoc +444 -0
- data/readme-docs/architecture.adoc +449 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +442 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +325 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +617 -0
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "constants"
|
|
4
|
+
require_relative "file_collector"
|
|
5
|
+
require_relative "stream_compressor"
|
|
6
|
+
require_relative "header_writer"
|
|
7
|
+
require_relative "models/file_entry"
|
|
8
|
+
require_relative "split_archive_writer"
|
|
9
|
+
require_relative "header_encryptor"
|
|
10
|
+
require_relative "encrypted_header"
|
|
11
|
+
require_relative "../../models/split_options"
|
|
12
|
+
require_relative "../../algorithms/lzma2"
|
|
13
|
+
require "stringio"
|
|
14
|
+
|
|
15
|
+
module Omnizip
|
|
16
|
+
module Formats
|
|
17
|
+
module SevenZip
|
|
18
|
+
# .7z archive writer - 7-Zip compatible implementation
|
|
19
|
+
#
|
|
20
|
+
# Creates archives that are fully compatible with official 7-Zip (7zz command).
|
|
21
|
+
#
|
|
22
|
+
# Archive structure:
|
|
23
|
+
# - Start Header (32 bytes)
|
|
24
|
+
# - LZMA2 compressed file data
|
|
25
|
+
# - UNCOMPRESSED Next Header metadata (properties: kHeader, kPackInfo, etc.)
|
|
26
|
+
# - Metadata footer (filename, attributes, timestamps)
|
|
27
|
+
class Writer
|
|
28
|
+
include Constants
|
|
29
|
+
|
|
30
|
+
# Constants for array literals used in loops (RuboCop Performance/CollectionLiteralInLoop)
|
|
31
|
+
COPY_MAIN_BYTE = [0x01].pack("C").freeze
|
|
32
|
+
COPY_METHOD_ID = [0x00].pack("C").freeze
|
|
33
|
+
NULL_TERMINATOR = [0x00, 0x00].pack("CC").freeze
|
|
34
|
+
FILE_ATTRIBUTE_ARCHIVE = [0x20].pack("V").freeze
|
|
35
|
+
|
|
36
|
+
attr_reader :output_path, :options, :entries
|
|
37
|
+
|
|
38
|
+
def initialize(output_path, options = {})
|
|
39
|
+
@output_path = output_path
|
|
40
|
+
@options = {
|
|
41
|
+
algorithm: :lzma2,
|
|
42
|
+
level: 5,
|
|
43
|
+
dict_size: 8 * 1024 * 1024, # 8MB default dictionary
|
|
44
|
+
solid: true, # Solid mode for LZMA2 compression
|
|
45
|
+
filters: [],
|
|
46
|
+
encrypt_headers: false,
|
|
47
|
+
}.merge(options)
|
|
48
|
+
@collector = FileCollector.new
|
|
49
|
+
@entries = []
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def add_file(file_path, archive_path = nil)
|
|
53
|
+
@collector.add_path(file_path, archive_path: archive_path,
|
|
54
|
+
recursive: false)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def add_directory(dir_path, recursive = true)
|
|
58
|
+
@collector.add_path(dir_path, recursive: recursive)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def add_files(pattern)
|
|
62
|
+
@collector.add_glob(pattern)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def add_data(archive_path, data, options = {})
|
|
66
|
+
data_str = data.is_a?(String) ? data : data.read
|
|
67
|
+
|
|
68
|
+
entry = Models::FileEntry.new
|
|
69
|
+
entry.name = archive_path
|
|
70
|
+
entry.source_path = nil
|
|
71
|
+
entry.size = data_str.bytesize
|
|
72
|
+
entry.mtime = Time.now
|
|
73
|
+
entry.has_stream = true
|
|
74
|
+
entry.instance_variable_set(:@_data, data_str)
|
|
75
|
+
# Store compression options for later use
|
|
76
|
+
entry.compression_options = options if entry.respond_to?(:compression_options=)
|
|
77
|
+
@entries << entry
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def write
|
|
81
|
+
# Collect any files from the collector (if add_file/add_directory was used)
|
|
82
|
+
collected_entries = @collector.collect_files
|
|
83
|
+
# Merge with entries added via add_data
|
|
84
|
+
@entries.concat(collected_entries)
|
|
85
|
+
|
|
86
|
+
# Check if split archive requested
|
|
87
|
+
if @options[:volume_size]
|
|
88
|
+
write_split_archive
|
|
89
|
+
else
|
|
90
|
+
File.open(@output_path, "wb") do |io|
|
|
91
|
+
write_archive(io)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
# Write split archive (delegates to SplitArchiveWriter)
|
|
99
|
+
def write_split_archive
|
|
100
|
+
split_options = Omnizip::Models::SplitOptions.new
|
|
101
|
+
split_options.volume_size = @options[:volume_size]
|
|
102
|
+
|
|
103
|
+
writer = SplitArchiveWriter.new(@output_path, @options, split_options)
|
|
104
|
+
|
|
105
|
+
# Add all collected files
|
|
106
|
+
@entries.each do |entry|
|
|
107
|
+
if entry.directory?
|
|
108
|
+
# Directories are already in entries
|
|
109
|
+
next
|
|
110
|
+
elsif entry.source_path
|
|
111
|
+
writer.add_file(entry.source_path, entry.name)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
writer.write
|
|
116
|
+
@entries = writer.entries
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def write_archive(io)
|
|
120
|
+
# Reserve space for start header
|
|
121
|
+
io.write("\0" * START_HEADER_SIZE)
|
|
122
|
+
|
|
123
|
+
# Step 1: Collect file data
|
|
124
|
+
file_data = collect_file_data
|
|
125
|
+
|
|
126
|
+
# Step 2: Build compressed data based on mode
|
|
127
|
+
if @options[:solid]
|
|
128
|
+
# Solid mode: compress all data together with LZMA2
|
|
129
|
+
packed_data, packed_sizes = build_solid_packed_data(file_data)
|
|
130
|
+
else
|
|
131
|
+
# Non-solid mode: each file stored separately (COPY method - no compression)
|
|
132
|
+
packed_data, packed_sizes = build_non_solid_packed_data(file_data)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Step 3: Build Next Header properties
|
|
136
|
+
# This includes kHeader, MAIN_STREAMS_INFO, FILES_INFO, etc.
|
|
137
|
+
next_header_data = build_next_header_properties(file_data, packed_sizes)
|
|
138
|
+
|
|
139
|
+
# Step 4: Write the complete data section
|
|
140
|
+
# Note: CRC is stored in StartHeader, NOT appended to Next Header
|
|
141
|
+
io.write(packed_data) # Packed file data
|
|
142
|
+
io.write(next_header_data) # Next Header (CRC is in StartHeader)
|
|
143
|
+
|
|
144
|
+
# Step 5: Write Start Header
|
|
145
|
+
# Next Header starts after the packed data
|
|
146
|
+
# The offset is RELATIVE to the END of the StartHeader (byte 32)
|
|
147
|
+
next_header_offset = packed_data.bytesize
|
|
148
|
+
|
|
149
|
+
# Next Header size is the size of the Next Header data WITHOUT the CRC32
|
|
150
|
+
# (CRC32 is appended after the header data, not included in size)
|
|
151
|
+
next_header_size = next_header_data.bytesize
|
|
152
|
+
|
|
153
|
+
write_start_header(io, next_header_offset, next_header_size, next_header_data)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Build packed data for solid mode (LZMA2 compression)
|
|
157
|
+
def build_solid_packed_data(file_data)
|
|
158
|
+
lzma2_chunk, compressed_size = build_lzma2_compressed_chunk(file_data[:data])
|
|
159
|
+
[lzma2_chunk, [compressed_size]]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Build packed data for non-solid mode (COPY - no compression)
|
|
163
|
+
def build_non_solid_packed_data(file_data)
|
|
164
|
+
# For non-solid mode, concatenate raw file data without compression
|
|
165
|
+
packed_data = String.new(encoding: "BINARY")
|
|
166
|
+
packed_sizes = []
|
|
167
|
+
|
|
168
|
+
file_data[:streams].each do |stream|
|
|
169
|
+
packed_data << stream[:data]
|
|
170
|
+
packed_sizes << stream[:size]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
[packed_data, packed_sizes]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def collect_file_data
|
|
177
|
+
files_with_data = @entries.select(&:has_stream?)
|
|
178
|
+
|
|
179
|
+
if @options[:solid]
|
|
180
|
+
# Solid mode: combine all files into one stream
|
|
181
|
+
combined = String.new(encoding: "BINARY")
|
|
182
|
+
total_size = 0
|
|
183
|
+
|
|
184
|
+
files_with_data.each do |entry|
|
|
185
|
+
data = entry.instance_variable_get(:@_data) || File.binread(entry.source_path)
|
|
186
|
+
combined << data
|
|
187
|
+
total_size += data.bytesize
|
|
188
|
+
|
|
189
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
190
|
+
crc.update(data)
|
|
191
|
+
entry.crc = crc.finalize
|
|
192
|
+
entry.size = data.bytesize
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
{ data: combined, total_size: total_size, streams: [{ data: combined, size: total_size }] }
|
|
196
|
+
else
|
|
197
|
+
# Non-solid mode: each file gets its own stream
|
|
198
|
+
streams = []
|
|
199
|
+
total_size = 0
|
|
200
|
+
|
|
201
|
+
files_with_data.each do |entry|
|
|
202
|
+
data = entry.instance_variable_get(:@_data) || File.binread(entry.source_path)
|
|
203
|
+
|
|
204
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
205
|
+
crc.update(data)
|
|
206
|
+
entry.crc = crc.finalize
|
|
207
|
+
entry.size = data.bytesize
|
|
208
|
+
|
|
209
|
+
streams << { data: data, size: data.bytesize }
|
|
210
|
+
total_size += data.bytesize
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Combine all data for writing
|
|
214
|
+
combined = streams.map { |s| s[:data] }.join
|
|
215
|
+
{ data: combined, total_size: total_size, streams: streams }
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def build_lzma2_compressed_chunk(data)
|
|
220
|
+
return ["", 0] if data.nil? || data.empty?
|
|
221
|
+
|
|
222
|
+
# Use actual LZMA2 compression via 7-Zip SDK encoder
|
|
223
|
+
compressed = compress_with_lzma2(data)
|
|
224
|
+
[compressed, compressed.bytesize]
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def build_next_header_properties(file_data, packed_sizes)
|
|
228
|
+
metadata = String.new(encoding: "BINARY")
|
|
229
|
+
unpack_size = file_data[:total_size]
|
|
230
|
+
num_files = @entries.size
|
|
231
|
+
solid = @options[:solid]
|
|
232
|
+
|
|
233
|
+
# kHeader property (0x01)
|
|
234
|
+
metadata << [PropertyId::HEADER].pack("C")
|
|
235
|
+
|
|
236
|
+
# kMainStreamsInfo property (0x04) - WRAPPER for stream info
|
|
237
|
+
metadata << [PropertyId::MAIN_STREAMS_INFO].pack("C")
|
|
238
|
+
|
|
239
|
+
if solid
|
|
240
|
+
# Solid mode: one pack stream, one folder
|
|
241
|
+
# packed_sizes is a single-element array with compressed size
|
|
242
|
+
compressed_size = packed_sizes.first
|
|
243
|
+
build_solid_streams_info(metadata, unpack_size, compressed_size, num_files)
|
|
244
|
+
else
|
|
245
|
+
# Non-solid mode: one pack stream per file, one folder per file
|
|
246
|
+
build_non_solid_streams_info(metadata, file_data[:streams])
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# kEnd for MainStreamsInfo
|
|
250
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
251
|
+
|
|
252
|
+
# FILES_INFO section
|
|
253
|
+
build_files_info(metadata)
|
|
254
|
+
|
|
255
|
+
# kEnd for Header (closes the entire Next Header)
|
|
256
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
257
|
+
|
|
258
|
+
# Encrypt headers if requested
|
|
259
|
+
if @options[:encrypt_headers]
|
|
260
|
+
metadata = encrypt_header(metadata)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
metadata
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Encrypt header data
|
|
267
|
+
#
|
|
268
|
+
# @param header_data [String] Unencrypted header
|
|
269
|
+
# @return [String] Encrypted header with metadata
|
|
270
|
+
def encrypt_header(header_data)
|
|
271
|
+
unless @options[:password]
|
|
272
|
+
raise "Password required for header encryption"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
encryptor = HeaderEncryptor.new(@options[:password])
|
|
276
|
+
result = encryptor.encrypt(header_data)
|
|
277
|
+
|
|
278
|
+
# Create encrypted header structure
|
|
279
|
+
encrypted_header = EncryptedHeader.new(
|
|
280
|
+
encrypted_data: result[:data],
|
|
281
|
+
salt: result[:salt],
|
|
282
|
+
iv: result[:iv],
|
|
283
|
+
original_size: result[:size],
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
encrypted_header.to_binary
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def build_solid_streams_info(metadata, unpack_size, compressed_size, num_files)
|
|
290
|
+
# kPackInfo property (0x06)
|
|
291
|
+
metadata << [PropertyId::PACK_INFO].pack("C")
|
|
292
|
+
metadata << write_number(0) # Pack position
|
|
293
|
+
metadata << write_number(1) # Number of pack streams
|
|
294
|
+
|
|
295
|
+
# kSize property
|
|
296
|
+
metadata << [PropertyId::SIZE].pack("C")
|
|
297
|
+
metadata << write_number(compressed_size)
|
|
298
|
+
|
|
299
|
+
# kEnd for PackInfo
|
|
300
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
301
|
+
|
|
302
|
+
# kUnpackInfo property (0x07)
|
|
303
|
+
metadata << [PropertyId::UNPACK_INFO].pack("C")
|
|
304
|
+
|
|
305
|
+
# kFolder property (0x0B)
|
|
306
|
+
metadata << [PropertyId::FOLDER].pack("C")
|
|
307
|
+
metadata << write_number(1) # Number of folders
|
|
308
|
+
|
|
309
|
+
# External flag (0 = inline, folders follow)
|
|
310
|
+
metadata << [0].pack("C")
|
|
311
|
+
|
|
312
|
+
# Folder definitions
|
|
313
|
+
build_folder_coder(metadata)
|
|
314
|
+
|
|
315
|
+
# kCodersUnpackSize - comes AFTER all folder definitions
|
|
316
|
+
metadata << [PropertyId::CODERS_UNPACK_SIZE].pack("C")
|
|
317
|
+
metadata << write_number(unpack_size)
|
|
318
|
+
|
|
319
|
+
# kEnd for UnpackInfo
|
|
320
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
321
|
+
|
|
322
|
+
# kSubStreamsInfo - for solid archives with multiple files
|
|
323
|
+
metadata << [PropertyId::SUBSTREAMS_INFO].pack("C")
|
|
324
|
+
if num_files > 1
|
|
325
|
+
|
|
326
|
+
# NUM_UNPACK_STREAM - number of files in this folder
|
|
327
|
+
metadata << [PropertyId::NUM_UNPACK_STREAM].pack("C")
|
|
328
|
+
metadata << write_number(num_files)
|
|
329
|
+
|
|
330
|
+
# SIZE - size of each file's data (except the last one)
|
|
331
|
+
# Per 7-Zip spec: only write numSubstreams-1 sizes, last is calculated
|
|
332
|
+
# from folder's unpack size minus sum of written sizes
|
|
333
|
+
metadata << [PropertyId::SIZE].pack("C")
|
|
334
|
+
@entries[0..-2].each do |entry| # All except last
|
|
335
|
+
metadata << write_number(entry.size)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# CRCs
|
|
339
|
+
metadata << [PropertyId::CRC].pack("C")
|
|
340
|
+
metadata << [1].pack("C") # All defined
|
|
341
|
+
@entries.each do |entry|
|
|
342
|
+
# Use 0 for entries without CRC (empty files)
|
|
343
|
+
crc = entry.crc || 0
|
|
344
|
+
metadata << [crc].pack("V")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
else
|
|
348
|
+
# Single file: CRC goes in SubStreamsInfo
|
|
349
|
+
metadata << [PropertyId::CRC].pack("C")
|
|
350
|
+
metadata << [1].pack("C") # All defined
|
|
351
|
+
# Use 0 for entries without CRC (empty files)
|
|
352
|
+
crc = @entries.first&.crc || 0
|
|
353
|
+
metadata << [crc].pack("V")
|
|
354
|
+
end
|
|
355
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def build_non_solid_streams_info(metadata, streams)
|
|
359
|
+
num_streams = streams.size
|
|
360
|
+
|
|
361
|
+
# kPackInfo property (0x06)
|
|
362
|
+
metadata << [PropertyId::PACK_INFO].pack("C")
|
|
363
|
+
metadata << write_number(0) # Pack position
|
|
364
|
+
metadata << write_number(num_streams) # Number of pack streams
|
|
365
|
+
|
|
366
|
+
# kSize property - sizes of each pack stream
|
|
367
|
+
metadata << [PropertyId::SIZE].pack("C")
|
|
368
|
+
streams.each do |stream|
|
|
369
|
+
metadata << write_number(stream[:size])
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# kEnd for PackInfo
|
|
373
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
374
|
+
|
|
375
|
+
# kUnpackInfo property (0x07)
|
|
376
|
+
metadata << [PropertyId::UNPACK_INFO].pack("C")
|
|
377
|
+
|
|
378
|
+
# kFolder property (0x0B)
|
|
379
|
+
metadata << [PropertyId::FOLDER].pack("C")
|
|
380
|
+
metadata << write_number(num_streams) # Number of folders
|
|
381
|
+
|
|
382
|
+
# External flag for all folders
|
|
383
|
+
metadata << [0].pack("C")
|
|
384
|
+
|
|
385
|
+
# Folder definitions (coders only, no sizes yet)
|
|
386
|
+
streams.each do |_stream|
|
|
387
|
+
# Number of coders
|
|
388
|
+
metadata << write_number(1)
|
|
389
|
+
# Coder info for COPY
|
|
390
|
+
# MainByte format: bits 0-3 = num bytes for CodecID (0-15), bit 4 = is_complex, bits 5-7 = num props
|
|
391
|
+
# For COPY: 1 byte CodecID (0x00), no properties
|
|
392
|
+
metadata << COPY_MAIN_BYTE # MainByte: 1 byte for CodecID, no props
|
|
393
|
+
metadata << COPY_METHOD_ID # Method ID: COPY = 0x00
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# kCodersUnpackSize - comes after ALL folder definitions
|
|
397
|
+
metadata << [PropertyId::CODERS_UNPACK_SIZE].pack("C")
|
|
398
|
+
streams.each do |stream|
|
|
399
|
+
metadata << write_number(stream[:size])
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# kEnd for UnpackInfo
|
|
403
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
404
|
+
|
|
405
|
+
# kSubStreamsInfo - CRCs for each file
|
|
406
|
+
metadata << [PropertyId::SUBSTREAMS_INFO].pack("C")
|
|
407
|
+
metadata << [PropertyId::CRC].pack("C")
|
|
408
|
+
metadata << [1].pack("C") # All defined
|
|
409
|
+
@entries.each do |entry|
|
|
410
|
+
# Use 0 for entries without CRC (empty files)
|
|
411
|
+
crc = entry.crc || 0
|
|
412
|
+
metadata << [crc].pack("V")
|
|
413
|
+
end
|
|
414
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def build_folder_coder(metadata)
|
|
418
|
+
# Number of coders
|
|
419
|
+
metadata << write_number(1)
|
|
420
|
+
|
|
421
|
+
# Coder info for LZMA2 (method 0x21)
|
|
422
|
+
# MainByte format:
|
|
423
|
+
# bits 0-3 = num bytes for CodecID (1 for 0x21)
|
|
424
|
+
# bit 4 = IsComplexCoder (0)
|
|
425
|
+
# bits 5-7 = num property bytes (we set to 0 and write size separately)
|
|
426
|
+
# Per 7-Zip SDK, bits 5-7 indicate if there ARE properties (non-zero = has props)
|
|
427
|
+
# MainByte = 0x21 means: 1 byte for CodecID + has properties
|
|
428
|
+
metadata << [0x21].pack("C") # MainByte: 1 byte for CodecID + has properties
|
|
429
|
+
metadata << [0x21].pack("C") # Method ID: LZMA2 = 0x21
|
|
430
|
+
|
|
431
|
+
# LZMA2 property byte encodes dictionary size
|
|
432
|
+
dict_size = @options[:dict_size] || (8 * 1024 * 1024)
|
|
433
|
+
prop_byte = encode_lzma2_dict_size(dict_size)
|
|
434
|
+
|
|
435
|
+
# 7-Zip format: write property SIZE (as VLI), then property bytes
|
|
436
|
+
metadata << write_number(1) # PropsSize = 1 byte
|
|
437
|
+
metadata << [prop_byte].pack("C") # Property byte
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def build_files_info(metadata)
|
|
441
|
+
# kFilesInfo property (0x05)
|
|
442
|
+
metadata << [PropertyId::FILES_INFO].pack("C")
|
|
443
|
+
|
|
444
|
+
# Number of files
|
|
445
|
+
metadata << write_number(@entries.size)
|
|
446
|
+
|
|
447
|
+
# Build NAME property (0x11)
|
|
448
|
+
# Format: [NAME] [size] [external] [UTF-16LE names with null terminators]
|
|
449
|
+
name_data = String.new(encoding: "BINARY")
|
|
450
|
+
|
|
451
|
+
@entries.each do |entry|
|
|
452
|
+
# Encode name as UTF-16LE and force to BINARY
|
|
453
|
+
name_utf16le = entry.name.encode("UTF-16LE").b
|
|
454
|
+
# Add null terminator (2 bytes)
|
|
455
|
+
name_data << name_utf16le
|
|
456
|
+
name_data << NULL_TERMINATOR
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
metadata << [PropertyId::NAME].pack("C")
|
|
460
|
+
metadata << write_number(name_data.bytesize + 1) # +1 for External byte
|
|
461
|
+
metadata << [0].pack("C") # External = 0 (inline)
|
|
462
|
+
metadata << name_data
|
|
463
|
+
|
|
464
|
+
# Build MTIME property (0x14) - MUST come before WIN_ATTRIB per 7-Zip spec
|
|
465
|
+
# Format: [MTIME] [size] [defined bits] [external] [FILETIME values]
|
|
466
|
+
time_data = String.new(encoding: "BINARY")
|
|
467
|
+
|
|
468
|
+
@entries.each do |entry|
|
|
469
|
+
unix_time = entry.mtime.to_i
|
|
470
|
+
windows_time = (unix_time + 11_644_473_600) * 10_000_000
|
|
471
|
+
time_data << [windows_time].pack("Q<")
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
metadata << [PropertyId::MTIME].pack("C")
|
|
475
|
+
metadata << write_number(time_data.bytesize + 2) # +2 for all_defined and external bytes
|
|
476
|
+
metadata << [1].pack("C") # All defined
|
|
477
|
+
metadata << [0].pack("C") # External = 0 (inline)
|
|
478
|
+
metadata << time_data
|
|
479
|
+
|
|
480
|
+
# Build WIN_ATTRIB property (0x15) - comes after MTIME per 7-Zip spec
|
|
481
|
+
# Format: [WIN_ATTRIB] [size] [defined bits] [external] [attributes]
|
|
482
|
+
attr_data = String.new(encoding: "BINARY")
|
|
483
|
+
@entries.each do |_entry|
|
|
484
|
+
attr_data << FILE_ATTRIBUTE_ARCHIVE
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
metadata << [PropertyId::WIN_ATTRIB].pack("C")
|
|
488
|
+
metadata << write_number(attr_data.bytesize + 2) # +2 for all_defined and external bytes
|
|
489
|
+
metadata << [1].pack("C") # All defined
|
|
490
|
+
metadata << [0].pack("C") # External = 0 (inline)
|
|
491
|
+
metadata << attr_data
|
|
492
|
+
|
|
493
|
+
# kEnd for FilesInfo
|
|
494
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def compress_with_lzma2(data)
|
|
498
|
+
# Use 7-Zip SDK LZMA2 encoder for 7-Zip format
|
|
499
|
+
dict_size = [4096, data.bytesize].max
|
|
500
|
+
|
|
501
|
+
encoder = Omnizip::Implementations::SevenZip::LZMA2::Encoder.new(
|
|
502
|
+
dict_size: dict_size,
|
|
503
|
+
lc: 3,
|
|
504
|
+
lp: 0,
|
|
505
|
+
pb: 2,
|
|
506
|
+
standalone: false, # No property byte (raw mode)
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
encoder.encode(data)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
# Encode dictionary size to LZMA2 property byte
|
|
513
|
+
#
|
|
514
|
+
# LZMA2 property byte encoding (per XZ spec, same for 7-Zip):
|
|
515
|
+
# dict_size = (2 | (props & 1)) << (props / 2 + 11)
|
|
516
|
+
#
|
|
517
|
+
# This gives sizes from 4KB (props=0) to 4GB (props=40)
|
|
518
|
+
#
|
|
519
|
+
# @param dict_size [Integer] Dictionary size in bytes
|
|
520
|
+
# @return [Integer] Property byte (0-40)
|
|
521
|
+
def encode_lzma2_dict_size(dict_size)
|
|
522
|
+
# Find the smallest prop value that gives >= dict_size
|
|
523
|
+
(0..40).each do |prop|
|
|
524
|
+
# Decode formula: dict_size = (2 | (prop & 1)) << (prop / 2 + 11)
|
|
525
|
+
base = 2 | (prop & 1)
|
|
526
|
+
size = base << ((prop / 2) + 11)
|
|
527
|
+
return prop if size >= dict_size
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# Maximum property value
|
|
531
|
+
40
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def build_next_header_metadata(compressed_size, unpack_size)
|
|
535
|
+
metadata = String.new(encoding: "BINARY")
|
|
536
|
+
|
|
537
|
+
# kHeader property (0x01)
|
|
538
|
+
metadata << [PropertyId::HEADER].pack("C")
|
|
539
|
+
|
|
540
|
+
# kMainStreamsInfo property (0x04) - WRAPPER for stream info
|
|
541
|
+
metadata << [PropertyId::MAIN_STREAMS_INFO].pack("C")
|
|
542
|
+
|
|
543
|
+
# kPackInfo property (0x06)
|
|
544
|
+
metadata << [PropertyId::PACK_INFO].pack("C")
|
|
545
|
+
metadata << write_number(0) # Pack position
|
|
546
|
+
metadata << write_number(1) # Number of pack streams
|
|
547
|
+
|
|
548
|
+
# kSize property
|
|
549
|
+
metadata << [PropertyId::SIZE].pack("C")
|
|
550
|
+
metadata << write_number(compressed_size)
|
|
551
|
+
|
|
552
|
+
# kEnd for PackInfo
|
|
553
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
554
|
+
|
|
555
|
+
# kUnpackInfo property (0x07)
|
|
556
|
+
metadata << [PropertyId::UNPACK_INFO].pack("C")
|
|
557
|
+
|
|
558
|
+
# kFolder property (0x0B)
|
|
559
|
+
metadata << [PropertyId::FOLDER].pack("C")
|
|
560
|
+
metadata << write_number(1) # Number of folders
|
|
561
|
+
|
|
562
|
+
# Folder content
|
|
563
|
+
metadata << [0].pack("C") # External flag (0 = inline)
|
|
564
|
+
|
|
565
|
+
# Number of coders
|
|
566
|
+
metadata << write_number(1)
|
|
567
|
+
|
|
568
|
+
# Coder info for LZMA2
|
|
569
|
+
# Method ID: LZMA2 = 0x21
|
|
570
|
+
# Main byte: 1 byte for ID + 0x00 for no properties
|
|
571
|
+
metadata << [1].pack("C") # 1 byte for ID
|
|
572
|
+
metadata << [0x21].pack("C") # LZMA2 method ID
|
|
573
|
+
|
|
574
|
+
# kCodersUnpackSize
|
|
575
|
+
metadata << [PropertyId::CODERS_UNPACK_SIZE].pack("C")
|
|
576
|
+
metadata << write_number(unpack_size)
|
|
577
|
+
|
|
578
|
+
# kSubStreamsInfo property (0x08)
|
|
579
|
+
metadata << [PropertyId::SUBSTREAMS_INFO].pack("C")
|
|
580
|
+
|
|
581
|
+
# kCRC property (0x0a)
|
|
582
|
+
metadata << [PropertyId::CRC].pack("C")
|
|
583
|
+
metadata << [1].pack("C") # All streams have CRC (1 = yes, 0 = no)
|
|
584
|
+
|
|
585
|
+
# EMPTY_STREAM (0x0e): Number of empty unpack streams
|
|
586
|
+
metadata << [PropertyId::EMPTY_STREAM].pack("C")
|
|
587
|
+
metadata << [0].pack("C") # 0 empty streams (file has data)
|
|
588
|
+
|
|
589
|
+
# kEnd for SubStreamsInfo
|
|
590
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
591
|
+
|
|
592
|
+
# kEnd for UnpackInfo
|
|
593
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
594
|
+
|
|
595
|
+
# kEnd for MainStreamsInfo
|
|
596
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
597
|
+
|
|
598
|
+
# kEnd for Header
|
|
599
|
+
metadata << [PropertyId::K_END].pack("C")
|
|
600
|
+
|
|
601
|
+
metadata
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
def build_metadata_footer
|
|
605
|
+
footer = String.new(encoding: "BINARY")
|
|
606
|
+
|
|
607
|
+
# Single loop to add filename, attributes, and timestamps for each entry
|
|
608
|
+
@entries.each do |entry|
|
|
609
|
+
# Add filename in UTF-16LE with null terminator
|
|
610
|
+
entry.name.encode("UTF-16LE").each_byte do |byte|
|
|
611
|
+
footer << [byte].pack("C")
|
|
612
|
+
end
|
|
613
|
+
footer << NULL_TERMINATOR
|
|
614
|
+
|
|
615
|
+
# Add file attributes (Windows FILE attributes)
|
|
616
|
+
footer << FILE_ATTRIBUTE_ARCHIVE
|
|
617
|
+
|
|
618
|
+
# Add Windows FILETIME for modification time
|
|
619
|
+
unix_time = entry.mtime.to_i
|
|
620
|
+
windows_time = (unix_time + 11_644_473_600) * 10_000_000
|
|
621
|
+
footer << [windows_time].pack("Q<")
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
footer
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def write_start_header(io, next_header_offset, next_header_size, next_header_data)
|
|
628
|
+
header = String.new(encoding: "BINARY")
|
|
629
|
+
|
|
630
|
+
# Signature (6 bytes)
|
|
631
|
+
header << SIGNATURE
|
|
632
|
+
|
|
633
|
+
# Version (2 bytes)
|
|
634
|
+
header << [MAJOR_VERSION, MINOR_VERSION].pack("CC")
|
|
635
|
+
|
|
636
|
+
# Calculate CRC for next header info
|
|
637
|
+
next_header_info = String.new(encoding: "BINARY")
|
|
638
|
+
next_header_info << [next_header_offset].pack("Q<")
|
|
639
|
+
next_header_info << [next_header_size].pack("Q<")
|
|
640
|
+
|
|
641
|
+
# Calculate CRC for next header
|
|
642
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
643
|
+
crc.update(next_header_data)
|
|
644
|
+
next_header_crc = crc.finalize
|
|
645
|
+
next_header_info << [next_header_crc].pack("V")
|
|
646
|
+
|
|
647
|
+
# Calculate CRC for next header info
|
|
648
|
+
info_crc = Omnizip::Checksums::Crc32.new
|
|
649
|
+
info_crc.update(next_header_info)
|
|
650
|
+
|
|
651
|
+
# Start header CRC (4 bytes)
|
|
652
|
+
header << [info_crc.finalize].pack("V")
|
|
653
|
+
|
|
654
|
+
# Next header info (20 bytes)
|
|
655
|
+
header << next_header_info
|
|
656
|
+
|
|
657
|
+
# Write at the beginning
|
|
658
|
+
io.seek(0)
|
|
659
|
+
io.write(header)
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
# Encode variable-length integer (7-Zip VLI format)
|
|
663
|
+
#
|
|
664
|
+
# 7-Zip VLI encoding uses the first byte's high bits to determine
|
|
665
|
+
# the number of additional bytes:
|
|
666
|
+
# 0xxxxxxx : value = xxxxxxx (0-127)
|
|
667
|
+
# 10xxxxxx BYTE y[1] : value = (xxxxxx << 8) + y
|
|
668
|
+
# 110xxxxx BYTE y[2] : value = (xxxxx << 16) + y
|
|
669
|
+
# 1110xxxx BYTE y[3] : value = (xxxx << 24) + y
|
|
670
|
+
# ...up to 8 bytes total
|
|
671
|
+
def write_number(value)
|
|
672
|
+
# Single byte encoding (0-127)
|
|
673
|
+
return [value].pack("C") if value < 0x80
|
|
674
|
+
|
|
675
|
+
# Determine number of bytes needed using 7-Zip VLI thresholds
|
|
676
|
+
# 2 bytes: 128 - 16383 (0x80 - 0x3FFF encoded as 10xxxxxx + 1 byte)
|
|
677
|
+
# 3 bytes: 16384 - 2097151 (0x4000 - 0x1FFFFF)
|
|
678
|
+
# etc.
|
|
679
|
+
bytes_needed = case value
|
|
680
|
+
when 0x80..0x3FFF then 2
|
|
681
|
+
when 0x4000..0x1F_FFFF then 3
|
|
682
|
+
when 0x20_0000..0xFFF_FFFF then 4
|
|
683
|
+
when 0x1000_0000..0x7_FFFF_FFFF then 5
|
|
684
|
+
when 0x8_0000_0000..0x3FF_FFFF_FFFF then 6
|
|
685
|
+
when 0x400_0000_0000..0x1_FFFF_FFFF_FFFF then 7
|
|
686
|
+
else 8
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
result = String.new(encoding: "BINARY")
|
|
690
|
+
|
|
691
|
+
case bytes_needed
|
|
692
|
+
when 2
|
|
693
|
+
# 10xxxxxx pattern
|
|
694
|
+
first_byte = 0x80 | (value >> 8)
|
|
695
|
+
result << [first_byte].pack("C")
|
|
696
|
+
result << [value & 0xFF].pack("C")
|
|
697
|
+
when 3
|
|
698
|
+
# 110xxxxx pattern
|
|
699
|
+
first_byte = 0xC0 | (value >> 16)
|
|
700
|
+
result << [first_byte].pack("C")
|
|
701
|
+
result << [(value >> 8) & 0xFF].pack("C")
|
|
702
|
+
result << [value & 0xFF].pack("C")
|
|
703
|
+
when 4
|
|
704
|
+
# 1110xxxx pattern
|
|
705
|
+
first_byte = 0xE0 | (value >> 24)
|
|
706
|
+
result << [first_byte].pack("C")
|
|
707
|
+
result << [(value >> 16) & 0xFF].pack("C")
|
|
708
|
+
result << [(value >> 8) & 0xFF].pack("C")
|
|
709
|
+
result << [value & 0xFF].pack("C")
|
|
710
|
+
when 5
|
|
711
|
+
# 11110xxx pattern
|
|
712
|
+
first_byte = 0xF0 | (value >> 32)
|
|
713
|
+
result << [first_byte].pack("C")
|
|
714
|
+
4.downto(1) { |i| result << [(value >> (8 * (i - 1))) & 0xFF].pack("C") }
|
|
715
|
+
when 6
|
|
716
|
+
# 111110xx pattern
|
|
717
|
+
first_byte = 0xF8 | (value >> 40)
|
|
718
|
+
result << [first_byte].pack("C")
|
|
719
|
+
5.downto(1) { |i| result << [(value >> (8 * (i - 1))) & 0xFF].pack("C") }
|
|
720
|
+
when 7
|
|
721
|
+
# 1111110x pattern
|
|
722
|
+
first_byte = 0xFC | (value >> 48)
|
|
723
|
+
result << [first_byte].pack("C")
|
|
724
|
+
6.downto(1) { |i| result << [(value >> (8 * (i - 1))) & 0xFF].pack("C") }
|
|
725
|
+
else
|
|
726
|
+
# 8 bytes: 11111110 or 11111111 prefix
|
|
727
|
+
result << if value < (1 << 56)
|
|
728
|
+
[0xFE].pack("C")
|
|
729
|
+
else
|
|
730
|
+
[0xFF].pack("C")
|
|
731
|
+
end
|
|
732
|
+
7.downto(0) { |i| result << [(value >> (8 * i)) & 0xFF].pack("C") }
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
result
|
|
736
|
+
end
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
end
|
|
740
|
+
end
|