omnizip 0.4.7-arm-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +293 -0
- data/CHANGELOG.md +1647 -0
- data/CONTEXT.md +68 -0
- data/COPYING +502 -0
- data/Gemfile +19 -0
- data/LICENSE +12 -0
- data/README.adoc +1327 -0
- data/Rakefile +15 -0
- data/TODO.refactor/00-overview.md +67 -0
- data/TODO.refactor/01-registry-base-class.md +81 -0
- data/TODO.refactor/02-error-hierarchy.md +57 -0
- data/TODO.refactor/03-autoload-entry-point.md +69 -0
- data/TODO.refactor/04-filter-base-consolidation.md +49 -0
- data/TODO.refactor/05-send-private-methods.md +64 -0
- data/TODO.refactor/06-instance-variable-access.md +69 -0
- data/TODO.refactor/07-respond-to-replacement.md +237 -0
- data/TODO.refactor/08-spec-doubles.md +27 -0
- data/TODO.refactor/09-cli-shared-module.md +35 -0
- data/TODO.refactor/10-format-detector-ocp.md +34 -0
- data/TODO.refactor/11-convenience-decoupling.md +38 -0
- data/TODO.refactor/12-thread-safety.md +8 -0
- data/TODO.refactor/13-lutaml-model-migration.md +158 -0
- data/TODO.refactor/14-add-missing-specs.md +25 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +92 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +263 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +183 -0
- data/docs/getting-started/your-first-archive.adoc +219 -0
- data/docs/guides/advanced-features/encryption.adoc +277 -0
- data/docs/guides/advanced-features/index.adoc +59 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +144 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +124 -0
- data/docs/guides/advanced-features/streaming.adoc +142 -0
- data/docs/guides/archive-formats/gzip-format.adoc +105 -0
- data/docs/guides/archive-formats/index.adoc +160 -0
- data/docs/guides/archive-formats/ole-format.adoc +151 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/rpm-format.adoc +108 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +114 -0
- data/docs/guides/compression-algorithms/deflate.adoc +313 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +394 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +244 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +313 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +296 -0
- data/docs/guides/creating-archives.adoc +348 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +228 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +187 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/rust-backend.md +0 -0
- data/docs/troubleshooting/index.adoc +207 -0
- data/docs/xar_format.md +216 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +176 -0
- data/lib/omnizip/algorithm_registry.rb +65 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +236 -0
- data/lib/omnizip/algorithms/bzip2/bz2.rb +687 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +146 -0
- data/lib/omnizip/algorithms/bzip2.rb +108 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +37 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +45 -0
- data/lib/omnizip/algorithms/deflate.rb +135 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +151 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +95 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +352 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +140 -0
- data/lib/omnizip/algorithms/deflate64.rb +118 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +146 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +70 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +326 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +139 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +172 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +204 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +208 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +368 -0
- data/lib/omnizip/algorithms/lzma/lzma1_encoder.rb +105 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +233 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +84 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +159 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +82 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +274 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +202 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +323 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +420 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +640 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +224 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +167 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +258 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +314 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +108 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +1311 -0
- data/lib/omnizip/algorithms/lzma.rb +266 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +180 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +38 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +145 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +165 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +122 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +133 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +151 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +127 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +153 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +245 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +146 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +32 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +104 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +135 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +246 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +122 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +142 -0
- data/lib/omnizip/algorithms/registration.rb +28 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +158 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +249 -0
- data/lib/omnizip/algorithms/zstandard/dictionary.rb +73 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +294 -0
- data/lib/omnizip/algorithms/zstandard/frame/block.rb +143 -0
- data/lib/omnizip/algorithms/zstandard/frame/header.rb +233 -0
- data/lib/omnizip/algorithms/zstandard/frame.rb +33 -0
- data/lib/omnizip/algorithms/zstandard/fse/bitstream.rb +183 -0
- data/lib/omnizip/algorithms/zstandard/fse/encoder.rb +534 -0
- data/lib/omnizip/algorithms/zstandard/fse/interleaved.rb +92 -0
- data/lib/omnizip/algorithms/zstandard/fse/table.rb +188 -0
- data/lib/omnizip/algorithms/zstandard/fse/table_description.rb +203 -0
- data/lib/omnizip/algorithms/zstandard/fse.rb +53 -0
- data/lib/omnizip/algorithms/zstandard/huffman.rb +276 -0
- data/lib/omnizip/algorithms/zstandard/huffman_encoder.rb +327 -0
- data/lib/omnizip/algorithms/zstandard/ldm.rb +121 -0
- data/lib/omnizip/algorithms/zstandard/literals.rb +241 -0
- data/lib/omnizip/algorithms/zstandard/literals_encoder.rb +96 -0
- data/lib/omnizip/algorithms/zstandard/match_finder.rb +483 -0
- data/lib/omnizip/algorithms/zstandard/sequences.rb +350 -0
- data/lib/omnizip/algorithms/zstandard/sequences_encoder.rb +333 -0
- data/lib/omnizip/algorithms/zstandard/xxhash.rb +159 -0
- data/lib/omnizip/algorithms/zstandard.rb +210 -0
- data/lib/omnizip/algorithms.rb +35 -0
- data/lib/omnizip/archive/builder.rb +48 -0
- data/lib/omnizip/archive/reader_session.rb +67 -0
- data/lib/omnizip/archive.rb +86 -0
- data/lib/omnizip/archive_handler.rb +82 -0
- data/lib/omnizip/archive_handlers/cpio_handler.rb +47 -0
- data/lib/omnizip/archive_handlers/iso_handler.rb +54 -0
- data/lib/omnizip/archive_handlers/ole_handler.rb +37 -0
- data/lib/omnizip/archive_handlers/rar_handler.rb +96 -0
- data/lib/omnizip/archive_handlers/rpm_handler.rb +45 -0
- data/lib/omnizip/archive_handlers/seven_zip_handler.rb +89 -0
- data/lib/omnizip/archive_handlers/tar_handler.rb +75 -0
- data/lib/omnizip/archive_handlers/xar_handler.rb +49 -0
- data/lib/omnizip/archive_handlers/zip_handler.rb +115 -0
- data/lib/omnizip/archive_handlers.rb +18 -0
- data/lib/omnizip/backends.rb +160 -0
- data/lib/omnizip/buffer/memory_archive.rb +246 -0
- data/lib/omnizip/buffer/memory_extractor.rb +234 -0
- data/lib/omnizip/buffer/seven_zip_bridge.rb +171 -0
- data/lib/omnizip/buffer.rb +187 -0
- data/lib/omnizip/checksum_registry.rb +41 -0
- data/lib/omnizip/checksums/crc32.rb +101 -0
- data/lib/omnizip/checksums/crc64.rb +102 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/registration.rb +16 -0
- data/lib/omnizip/checksums/verifier.rb +130 -0
- data/lib/omnizip/checksums.rb +11 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +123 -0
- data/lib/omnizip/chunked.rb +123 -0
- data/lib/omnizip/cli/output_formatter.rb +112 -0
- data/lib/omnizip/cli.rb +604 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +245 -0
- data/lib/omnizip/commands/archive_extract_command.rb +231 -0
- data/lib/omnizip/commands/archive_list_command.rb +204 -0
- data/lib/omnizip/commands/archive_repair_command.rb +129 -0
- data/lib/omnizip/commands/archive_verify_command.rb +115 -0
- data/lib/omnizip/commands/compress_command.rb +115 -0
- data/lib/omnizip/commands/decompress_command.rb +149 -0
- data/lib/omnizip/commands/list_command.rb +51 -0
- data/lib/omnizip/commands/metadata_command.rb +215 -0
- data/lib/omnizip/commands/parity_create_command.rb +114 -0
- data/lib/omnizip/commands/parity_repair_command.rb +120 -0
- data/lib/omnizip/commands/parity_verify_command.rb +122 -0
- data/lib/omnizip/commands/profile_list_command.rb +54 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/commands.rb +21 -0
- data/lib/omnizip/convenience.rb +389 -0
- data/lib/omnizip/converter/conversion_registry.rb +50 -0
- data/lib/omnizip/converter/conversion_strategy.rb +108 -0
- data/lib/omnizip/converter/extract_repack_strategy.rb +51 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +43 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +54 -0
- data/lib/omnizip/converter.rb +124 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +104 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +100 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/crypto.rb +11 -0
- data/lib/omnizip/entry.rb +44 -0
- data/lib/omnizip/error.rb +110 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +95 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +96 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +68 -0
- data/lib/omnizip/extraction/filter_chain.rb +179 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +66 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +247 -0
- data/lib/omnizip/extraction.rb +115 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +117 -0
- data/lib/omnizip/filter.rb +141 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +113 -0
- data/lib/omnizip/filters/bcj.rb +277 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +263 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +58 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +91 -0
- data/lib/omnizip/filters/bcj2.rb +93 -0
- data/lib/omnizip/filters/bcj_arm.rb +174 -0
- data/lib/omnizip/filters/bcj_arm64.rb +242 -0
- data/lib/omnizip/filters/bcj_ia64.rb +194 -0
- data/lib/omnizip/filters/bcj_ppc.rb +188 -0
- data/lib/omnizip/filters/bcj_sparc.rb +174 -0
- data/lib/omnizip/filters/bcj_x86.rb +191 -0
- data/lib/omnizip/filters/delta.rb +195 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/filters.rb +20 -0
- data/lib/omnizip/format_detector.rb +166 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +159 -0
- data/lib/omnizip/formats/cpio/bounded_io.rb +66 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +389 -0
- data/lib/omnizip/formats/cpio/reader.rb +194 -0
- data/lib/omnizip/formats/cpio/writer.rb +232 -0
- data/lib/omnizip/formats/cpio.rb +132 -0
- data/lib/omnizip/formats/format_spec_loader.rb +228 -0
- data/lib/omnizip/formats/gzip.rb +236 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +321 -0
- data/lib/omnizip/formats/iso/directory_record.rb +162 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +194 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +319 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +543 -0
- data/lib/omnizip/formats/iso.rb +133 -0
- data/lib/omnizip/formats/lzip.rb +161 -0
- data/lib/omnizip/formats/lzma_alone.rb +149 -0
- data/lib/omnizip/formats/msi/cab_extractor.rb +181 -0
- data/lib/omnizip/formats/msi/constants.rb +157 -0
- data/lib/omnizip/formats/msi/directory_resolver.rb +188 -0
- data/lib/omnizip/formats/msi/entry.rb +134 -0
- data/lib/omnizip/formats/msi/reader.rb +301 -0
- data/lib/omnizip/formats/msi/string_pool.rb +246 -0
- data/lib/omnizip/formats/msi/table_parser.rb +244 -0
- data/lib/omnizip/formats/msi.rb +73 -0
- data/lib/omnizip/formats/ole/allocation_table.rb +263 -0
- data/lib/omnizip/formats/ole/constants.rb +61 -0
- data/lib/omnizip/formats/ole/dirent.rb +439 -0
- data/lib/omnizip/formats/ole/header.rb +199 -0
- data/lib/omnizip/formats/ole/ranges_io.rb +347 -0
- data/lib/omnizip/formats/ole/storage.rb +482 -0
- data/lib/omnizip/formats/ole/types/variant.rb +338 -0
- data/lib/omnizip/formats/ole.rb +143 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +240 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +192 -0
- data/lib/omnizip/formats/rar/block_parser.rb +282 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +210 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +212 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +154 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman.rb +25 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +103 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +238 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +250 -0
- data/lib/omnizip/formats/rar/compression/ppmd.rb +16 -0
- data/lib/omnizip/formats/rar/compression.rb +17 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +88 -0
- data/lib/omnizip/formats/rar/decompressor.rb +316 -0
- data/lib/omnizip/formats/rar/external_writer.rb +311 -0
- data/lib/omnizip/formats/rar/header.rb +209 -0
- data/lib/omnizip/formats/rar/license_validator.rb +111 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/models.rb +14 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +299 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +200 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +584 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +96 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +162 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +208 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +260 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +176 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +108 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +139 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +53 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +540 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +239 -0
- data/lib/omnizip/formats/rar/reader.rb +377 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +166 -0
- data/lib/omnizip/formats/rar/writer.rb +437 -0
- data/lib/omnizip/formats/rar.rb +193 -0
- data/lib/omnizip/formats/rar3/compressor.rb +71 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +64 -0
- data/lib/omnizip/formats/rar3/reader.rb +107 -0
- data/lib/omnizip/formats/rar3/writer.rb +220 -0
- data/lib/omnizip/formats/rar3.rb +15 -0
- data/lib/omnizip/formats/rar5/compressor.rb +71 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +64 -0
- data/lib/omnizip/formats/rar5/reader.rb +130 -0
- data/lib/omnizip/formats/rar5/writer.rb +65 -0
- data/lib/omnizip/formats/rar5.rb +15 -0
- data/lib/omnizip/formats/rpm/constants.rb +58 -0
- data/lib/omnizip/formats/rpm/entry.rb +106 -0
- data/lib/omnizip/formats/rpm/header.rb +106 -0
- data/lib/omnizip/formats/rpm/lead.rb +132 -0
- data/lib/omnizip/formats/rpm/tag.rb +228 -0
- data/lib/omnizip/formats/rpm/writer.rb +652 -0
- data/lib/omnizip/formats/rpm.rb +458 -0
- data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +274 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +169 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +110 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +139 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +143 -0
- data/lib/omnizip/formats/seven_zip/header.rb +102 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +132 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +425 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +65 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +67 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/models.rb +20 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +671 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +626 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +611 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +308 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +166 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +801 -0
- data/lib/omnizip/formats/seven_zip.rb +179 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +98 -0
- data/lib/omnizip/formats/tar/header.rb +206 -0
- data/lib/omnizip/formats/tar/reader.rb +119 -0
- data/lib/omnizip/formats/tar/writer.rb +212 -0
- data/lib/omnizip/formats/tar.rb +74 -0
- data/lib/omnizip/formats/xar/constants.rb +119 -0
- data/lib/omnizip/formats/xar/entry.rb +268 -0
- data/lib/omnizip/formats/xar/header.rb +223 -0
- data/lib/omnizip/formats/xar/reader.rb +400 -0
- data/lib/omnizip/formats/xar/toc.rb +570 -0
- data/lib/omnizip/formats/xar/writer.rb +461 -0
- data/lib/omnizip/formats/xar.rb +145 -0
- data/lib/omnizip/formats/xz/reader.rb +121 -0
- data/lib/omnizip/formats/xz/writer.rb +416 -0
- data/lib/omnizip/formats/xz.rb +210 -0
- data/lib/omnizip/formats/xz_const.rb +49 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +761 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +304 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +182 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +120 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +470 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +95 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +79 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +115 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +53 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +106 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl.rb +52 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +223 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +132 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +136 -0
- data/lib/omnizip/formats/zip/reader.rb +376 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +489 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +102 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +64 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +112 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/formats.rb +29 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/base.rb +19 -0
- data/lib/omnizip/implementations/rust/archive.rb +93 -0
- data/lib/omnizip/implementations/rust/library.rb +221 -0
- data/lib/omnizip/implementations/rust.rb +26 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +428 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +456 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +285 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +139 -0
- data/lib/omnizip/implementations/seven_zip/lzma.rb +21 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +56 -0
- data/lib/omnizip/implementations/seven_zip/lzma2.rb +12 -0
- data/lib/omnizip/implementations/seven_zip.rb +11 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +547 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +748 -0
- data/lib/omnizip/implementations/xz_utils/lzma2.rb +13 -0
- data/lib/omnizip/implementations/xz_utils.rb +10 -0
- data/lib/omnizip/implementations.rb +25 -0
- data/lib/omnizip/io/buffered_input.rb +147 -0
- data/lib/omnizip/io/buffered_output.rb +106 -0
- data/lib/omnizip/io/source.rb +143 -0
- data/lib/omnizip/io/stream_manager.rb +117 -0
- data/lib/omnizip/io.rb +15 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +129 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +153 -0
- data/lib/omnizip/metadata/metadata_editor.rb +169 -0
- data/lib/omnizip/metadata/metadata_registry.rb +71 -0
- data/lib/omnizip/metadata/metadata_validator.rb +101 -0
- data/lib/omnizip/metadata.rb +58 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +41 -0
- data/lib/omnizip/models/attribute_apply.rb +40 -0
- data/lib/omnizip/models/compression_options.rb +53 -0
- data/lib/omnizip/models/conversion_options.rb +79 -0
- data/lib/omnizip/models/conversion_result.rb +141 -0
- data/lib/omnizip/models/eta_result.rb +45 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +142 -0
- data/lib/omnizip/models/filter_config.rb +148 -0
- data/lib/omnizip/models/match_result.rb +141 -0
- data/lib/omnizip/models/optimization_suggestion.rb +95 -0
- data/lib/omnizip/models/parallel_options.rb +116 -0
- data/lib/omnizip/models/performance_result.rb +85 -0
- data/lib/omnizip/models/profile_report.rb +116 -0
- data/lib/omnizip/models/progress_options.rb +39 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +45 -0
- data/lib/omnizip/parallel/engine.rb +52 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +225 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +326 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +238 -0
- data/lib/omnizip/parallel/thread_pool.rb +60 -0
- data/lib/omnizip/parallel/worker_pool.rb +227 -0
- data/lib/omnizip/parallel.rb +156 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +194 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +71 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +131 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +111 -0
- data/lib/omnizip/parity/models/main_packet.rb +126 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +102 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +76 -0
- data/lib/omnizip/parity/models.rb +27 -0
- data/lib/omnizip/parity/par2_creator.rb +496 -0
- data/lib/omnizip/parity/par2_repairer.rb +409 -0
- data/lib/omnizip/parity/par2_verifier.rb +373 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +108 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +263 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +108 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +339 -0
- data/lib/omnizip/parity.rb +207 -0
- data/lib/omnizip/password/encryption_registry.rb +26 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +190 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +135 -0
- data/lib/omnizip/password.rb +88 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +173 -0
- data/lib/omnizip/pipe.rb +120 -0
- data/lib/omnizip/platform/ntfs_streams.rb +200 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +36 -0
- data/lib/omnizip/profile/balanced_profile.rb +31 -0
- data/lib/omnizip/profile/binary_profile.rb +33 -0
- data/lib/omnizip/profile/compression_profile.rb +157 -0
- data/lib/omnizip/profile/custom_profile.rb +155 -0
- data/lib/omnizip/profile/fast_profile.rb +31 -0
- data/lib/omnizip/profile/maximum_profile.rb +31 -0
- data/lib/omnizip/profile/profile_detector.rb +108 -0
- data/lib/omnizip/profile/profile_registry.rb +160 -0
- data/lib/omnizip/profile/text_profile.rb +33 -0
- data/lib/omnizip/profile.rb +261 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +158 -0
- data/lib/omnizip/profiler.rb +192 -0
- data/lib/omnizip/progress/callback_reporter.rb +34 -0
- data/lib/omnizip/progress/console_reporter.rb +59 -0
- data/lib/omnizip/progress/log_reporter.rb +89 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +215 -0
- data/lib/omnizip/progress/silent_reporter.rb +22 -0
- data/lib/omnizip/progress.rb +131 -0
- data/lib/omnizip/registry.rb +111 -0
- data/lib/omnizip/rubyzip_compat.rb +61 -0
- data/lib/omnizip/temp/safe_extract.rb +124 -0
- data/lib/omnizip/temp/temp_file.rb +123 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +184 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +160 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +270 -0
- data/lib/omnizip/zip/output_stream.rb +320 -0
- data/lib/omnizip/zip.rb +23 -0
- data/lib/omnizip.rb +157 -0
- data/lib/tasks/msi_test_files.rake +66 -0
- data/lib/tasks/platform_gems.rake +147 -0
- data/lib/tasks/rust_backend.rake +48 -0
- data/readme-docs/advanced-features.adoc +514 -0
- data/readme-docs/api-usage.adoc +445 -0
- data/readme-docs/architecture.adoc +450 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +446 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +316 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/vendor/libomnizip_ffi.so +0 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +768 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Rar
|
|
6
|
+
module Rar5
|
|
7
|
+
module MultiVolume
|
|
8
|
+
# Volume manager for multi-volume archive creation
|
|
9
|
+
#
|
|
10
|
+
# This class coordinates the creation of multi-volume RAR5 archives
|
|
11
|
+
# by managing volume splitting, file distribution, and volume writing.
|
|
12
|
+
#
|
|
13
|
+
# @example Create multi-volume archive
|
|
14
|
+
# manager = VolumeManager.new('archive.rar',
|
|
15
|
+
# max_volume_size: 10 * 1024 * 1024, # 10 MB
|
|
16
|
+
# compression: :lzma,
|
|
17
|
+
# level: 5
|
|
18
|
+
# )
|
|
19
|
+
# manager.add_file('large_file.dat')
|
|
20
|
+
# manager.create_volumes
|
|
21
|
+
class VolumeManager
|
|
22
|
+
# @return [String] Base archive path
|
|
23
|
+
attr_reader :base_path
|
|
24
|
+
|
|
25
|
+
# @return [VolumeOptions] Volume options
|
|
26
|
+
attr_reader :volume_options
|
|
27
|
+
|
|
28
|
+
# @return [Hash] Compression options
|
|
29
|
+
attr_reader :compression_options
|
|
30
|
+
|
|
31
|
+
# @return [Array<Hash>] Files to archive
|
|
32
|
+
attr_reader :files
|
|
33
|
+
|
|
34
|
+
# Initialize volume manager
|
|
35
|
+
#
|
|
36
|
+
# @param base_path [String] Base archive path (e.g., "archive.rar")
|
|
37
|
+
# @param options [Hash] Options
|
|
38
|
+
# @option options [Integer] :max_volume_size Maximum volume size in bytes
|
|
39
|
+
# @option options [String] :volume_naming Naming pattern ("part", "volume", "numeric")
|
|
40
|
+
# @option options [Symbol] :compression Compression method (:store, :lzma)
|
|
41
|
+
# @option options [Integer] :level Compression level (1-5)
|
|
42
|
+
# @option options [Boolean] :include_mtime Include modification time
|
|
43
|
+
# @option options [Boolean] :include_crc32 Include CRC32
|
|
44
|
+
def initialize(base_path, options = {})
|
|
45
|
+
@base_path = base_path
|
|
46
|
+
@volume_options = Models::VolumeOptions.new(
|
|
47
|
+
max_volume_size: options[:max_volume_size] || 104_857_600,
|
|
48
|
+
volume_naming: options[:volume_naming] || "part",
|
|
49
|
+
)
|
|
50
|
+
@volume_options.validate!
|
|
51
|
+
|
|
52
|
+
@compression_options = {
|
|
53
|
+
compression: options[:compression] || :store,
|
|
54
|
+
level: options[:level] || 3,
|
|
55
|
+
include_mtime: options[:include_mtime] || false,
|
|
56
|
+
include_crc32: options[:include_crc32] || false,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@files = []
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Add file to archive
|
|
63
|
+
#
|
|
64
|
+
# @param input_path [String] Path to file on disk
|
|
65
|
+
# @param archive_path [String, nil] Path within archive
|
|
66
|
+
# @return [void]
|
|
67
|
+
def add_file(input_path, archive_path = nil)
|
|
68
|
+
unless File.exist?(input_path)
|
|
69
|
+
raise ArgumentError,
|
|
70
|
+
"File not found: #{input_path}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
archive_path ||= File.basename(input_path)
|
|
74
|
+
|
|
75
|
+
@files << {
|
|
76
|
+
input: input_path,
|
|
77
|
+
archive: archive_path,
|
|
78
|
+
mtime: File.mtime(input_path),
|
|
79
|
+
stat: File.stat(input_path),
|
|
80
|
+
size: File.size(input_path),
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Add directory recursively
|
|
85
|
+
#
|
|
86
|
+
# @param dir_path [String] Directory path
|
|
87
|
+
# @param base_path [String, nil] Base path for relative names
|
|
88
|
+
# @return [void]
|
|
89
|
+
def add_directory(dir_path, base_path = nil)
|
|
90
|
+
base_path ||= dir_path
|
|
91
|
+
|
|
92
|
+
Dir.glob(File.join(dir_path, "**", "*")).each do |path|
|
|
93
|
+
next unless File.file?(path)
|
|
94
|
+
|
|
95
|
+
relative_path = path.sub(
|
|
96
|
+
/^#{Regexp.escape(base_path)}#{File::SEPARATOR}?/, ""
|
|
97
|
+
)
|
|
98
|
+
add_file(path, relative_path)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Create multi-volume archives
|
|
103
|
+
#
|
|
104
|
+
# @return [Array<String>] Paths to created volume files
|
|
105
|
+
def create_volumes
|
|
106
|
+
# Prepare file entries with compression
|
|
107
|
+
prepared_files = prepare_files
|
|
108
|
+
|
|
109
|
+
# Calculate file distribution across volumes
|
|
110
|
+
splitter = VolumeSplitter.new(max_volume_size: @volume_options.max_volume_size)
|
|
111
|
+
distribution = splitter.calculate_file_distribution(prepared_files)
|
|
112
|
+
|
|
113
|
+
# Create each volume
|
|
114
|
+
volume_paths = []
|
|
115
|
+
distribution.each_with_index do |file_indices, volume_idx|
|
|
116
|
+
volume_number = volume_idx + 1
|
|
117
|
+
is_last = (volume_idx == distribution.size - 1)
|
|
118
|
+
|
|
119
|
+
volume_path = VolumeWriter.volume_filename(
|
|
120
|
+
@base_path,
|
|
121
|
+
volume_number,
|
|
122
|
+
naming: @volume_options.volume_naming,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
write_volume(volume_path, volume_number, file_indices,
|
|
126
|
+
prepared_files, is_last)
|
|
127
|
+
volume_paths << volume_path
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
volume_paths
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Check if archive needs splitting
|
|
134
|
+
#
|
|
135
|
+
# @return [Boolean] true if multi-volume needed
|
|
136
|
+
def needs_splitting?
|
|
137
|
+
total_size = estimate_total_size
|
|
138
|
+
VolumeSplitter.needs_splitting?(total_size,
|
|
139
|
+
@volume_options.max_volume_size)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
# Prepare files with compression and metadata
|
|
145
|
+
#
|
|
146
|
+
# @return [Array<Hash>] Prepared file information
|
|
147
|
+
def prepare_files
|
|
148
|
+
@files.map do |file|
|
|
149
|
+
# Read and compress file data
|
|
150
|
+
data = File.binread(file[:input])
|
|
151
|
+
compression_method = select_compression_method(data)
|
|
152
|
+
compressed_data = compress_data(data, compression_method)
|
|
153
|
+
|
|
154
|
+
# Calculate CRC32 if needed
|
|
155
|
+
use_crc32 = @compression_options[:include_crc32] && compression_method == Compression::Store::METHOD
|
|
156
|
+
file_crc32 = use_crc32 ? CRC32.calculate(data) : nil
|
|
157
|
+
|
|
158
|
+
# Create file header
|
|
159
|
+
header = FileHeader.new(
|
|
160
|
+
filename: file[:archive],
|
|
161
|
+
file_size: data.bytesize,
|
|
162
|
+
compressed_size: compressed_data.bytesize,
|
|
163
|
+
compression_method: compression_method,
|
|
164
|
+
mtime: @compression_options[:include_mtime] ? file[:mtime] : nil,
|
|
165
|
+
crc32: file_crc32,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Estimate header size (approximate)
|
|
169
|
+
header_data = header.encode
|
|
170
|
+
header_size = header_data.bytesize
|
|
171
|
+
|
|
172
|
+
{
|
|
173
|
+
file: file,
|
|
174
|
+
header: header,
|
|
175
|
+
header_size: header_size,
|
|
176
|
+
compressed_data: compressed_data,
|
|
177
|
+
compressed_size: compressed_data.bytesize,
|
|
178
|
+
}
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Write single volume file
|
|
183
|
+
#
|
|
184
|
+
# @param volume_path [String] Volume file path
|
|
185
|
+
# @param volume_number [Integer] Volume number (1-based)
|
|
186
|
+
# @param file_indices [Array<Integer>] File indices to include
|
|
187
|
+
# @param prepared_files [Array<Hash>] All prepared files
|
|
188
|
+
# @param is_last [Boolean] Is this the last volume?
|
|
189
|
+
# @return [void]
|
|
190
|
+
def write_volume(volume_path, volume_number, file_indices,
|
|
191
|
+
prepared_files, is_last)
|
|
192
|
+
writer = VolumeWriter.new(volume_path,
|
|
193
|
+
volume_number: volume_number, is_last: is_last)
|
|
194
|
+
|
|
195
|
+
writer.write do |vol|
|
|
196
|
+
vol.write_signature
|
|
197
|
+
vol.write_main_header
|
|
198
|
+
|
|
199
|
+
file_indices.each do |idx|
|
|
200
|
+
prepared = prepared_files[idx]
|
|
201
|
+
vol.write_file_data(prepared[:header],
|
|
202
|
+
prepared[:compressed_data])
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
vol.write_end_header
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Select compression method
|
|
210
|
+
#
|
|
211
|
+
# @param data [String] File data
|
|
212
|
+
# @return [Integer] Compression method ID
|
|
213
|
+
def select_compression_method(data)
|
|
214
|
+
case @compression_options[:compression]
|
|
215
|
+
when :store
|
|
216
|
+
Compression::Store::METHOD
|
|
217
|
+
when :lzma
|
|
218
|
+
level = @compression_options[:level] || 3
|
|
219
|
+
Compression::Lzma.method_id(level)
|
|
220
|
+
when :auto
|
|
221
|
+
if data.bytesize < 1024
|
|
222
|
+
Compression::Store::METHOD
|
|
223
|
+
else
|
|
224
|
+
level = @compression_options[:level] || 3
|
|
225
|
+
Compression::Lzma.method_id(level)
|
|
226
|
+
end
|
|
227
|
+
else
|
|
228
|
+
Compression::Store::METHOD
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Compress data
|
|
233
|
+
#
|
|
234
|
+
# @param data [String] Data to compress
|
|
235
|
+
# @param method [Integer] Compression method ID
|
|
236
|
+
# @return [String] Compressed data
|
|
237
|
+
def compress_data(data, method)
|
|
238
|
+
if method == Compression::Store::METHOD
|
|
239
|
+
Compression::Store.compress(data)
|
|
240
|
+
else
|
|
241
|
+
level = method.clamp(1, 5)
|
|
242
|
+
Compression::Lzma.compress(data, level: level)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Estimate total archive size
|
|
247
|
+
#
|
|
248
|
+
# @return [Integer] Estimated size in bytes
|
|
249
|
+
def estimate_total_size
|
|
250
|
+
# Rough estimate: sum of file sizes + overhead
|
|
251
|
+
file_sizes = @files.sum { |f| f[:size] }
|
|
252
|
+
overhead = 1024 * @files.size # Header overhead per file
|
|
253
|
+
file_sizes + overhead
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Rar
|
|
6
|
+
module Rar5
|
|
7
|
+
module MultiVolume
|
|
8
|
+
# Volume splitter for multi-volume archives
|
|
9
|
+
#
|
|
10
|
+
# This class handles splitting compressed data streams across
|
|
11
|
+
# multiple volumes while respecting size boundaries and file atomicity.
|
|
12
|
+
#
|
|
13
|
+
# @example Split data across volumes
|
|
14
|
+
# splitter = VolumeSplitter.new(max_volume_size: 10_485_760)
|
|
15
|
+
# splitter.start_volume(1)
|
|
16
|
+
# splitter.write_to_current_volume(file1_data)
|
|
17
|
+
# if !splitter.can_fit_in_current_volume?(file2_data.bytesize)
|
|
18
|
+
# splitter.finalize_volume
|
|
19
|
+
# splitter.start_volume(2)
|
|
20
|
+
# end
|
|
21
|
+
class VolumeSplitter
|
|
22
|
+
# @return [Integer] Maximum size per volume in bytes
|
|
23
|
+
attr_reader :max_volume_size
|
|
24
|
+
|
|
25
|
+
# @return [Integer] Current volume number (1-based)
|
|
26
|
+
attr_reader :current_volume_number
|
|
27
|
+
|
|
28
|
+
# @return [Integer] Bytes written to current volume
|
|
29
|
+
attr_reader :current_volume_bytes
|
|
30
|
+
|
|
31
|
+
# @return [Array<Hash>] Volume metadata
|
|
32
|
+
attr_reader :volumes
|
|
33
|
+
|
|
34
|
+
# Minimum space reserved for headers (signature + main + end headers)
|
|
35
|
+
HEADER_OVERHEAD = 1024
|
|
36
|
+
|
|
37
|
+
# Initialize volume splitter
|
|
38
|
+
#
|
|
39
|
+
# @param max_volume_size [Integer] Maximum size per volume in bytes
|
|
40
|
+
def initialize(max_volume_size:)
|
|
41
|
+
@max_volume_size = max_volume_size
|
|
42
|
+
@current_volume_number = 0
|
|
43
|
+
@current_volume_bytes = 0
|
|
44
|
+
@current_volume_data = []
|
|
45
|
+
@volumes = []
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Start a new volume
|
|
49
|
+
#
|
|
50
|
+
# @param volume_number [Integer] Volume number (1-based)
|
|
51
|
+
# @return [void]
|
|
52
|
+
def start_volume(volume_number)
|
|
53
|
+
@current_volume_number = volume_number
|
|
54
|
+
@current_volume_bytes = HEADER_OVERHEAD # Reserve space for headers
|
|
55
|
+
@current_volume_data = []
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Check if data can fit in current volume
|
|
59
|
+
#
|
|
60
|
+
# @param data_size [Integer] Size of data to write
|
|
61
|
+
# @return [Boolean] true if data fits, false if new volume needed
|
|
62
|
+
def can_fit_in_current_volume?(data_size)
|
|
63
|
+
(@current_volume_bytes + data_size) <= @max_volume_size
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Get remaining space in current volume
|
|
67
|
+
#
|
|
68
|
+
# @return [Integer] Bytes available
|
|
69
|
+
def remaining_space
|
|
70
|
+
@max_volume_size - @current_volume_bytes
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Write data to current volume
|
|
74
|
+
#
|
|
75
|
+
# @param data [String] Data to write
|
|
76
|
+
# @return [void]
|
|
77
|
+
# @raise [RuntimeError] if no volume is active
|
|
78
|
+
# @raise [RuntimeError] if data doesn't fit
|
|
79
|
+
def write_to_current_volume(data)
|
|
80
|
+
raise Omnizip::CompressionError, "No active volume" if @current_volume_number.zero?
|
|
81
|
+
raise Omnizip::CompressionError, "Data doesn't fit in current volume" unless can_fit_in_current_volume?(data.bytesize)
|
|
82
|
+
|
|
83
|
+
@current_volume_data << data
|
|
84
|
+
@current_volume_bytes += data.bytesize
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Finalize current volume
|
|
88
|
+
#
|
|
89
|
+
# @return [Hash] Volume metadata
|
|
90
|
+
def finalize_volume
|
|
91
|
+
volume_info = {
|
|
92
|
+
number: @current_volume_number,
|
|
93
|
+
size: @current_volume_bytes,
|
|
94
|
+
data: @current_volume_data.join,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@volumes << volume_info
|
|
98
|
+
volume_info
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Calculate optimal file distribution across volumes
|
|
102
|
+
#
|
|
103
|
+
# This method determines how to distribute files across volumes
|
|
104
|
+
# to minimize volume count while respecting atomicity.
|
|
105
|
+
#
|
|
106
|
+
# @param files [Array<Hash>] File information with :compressed_size
|
|
107
|
+
# @return [Array<Array<Integer>>] Volume assignments (file indices per volume)
|
|
108
|
+
def calculate_file_distribution(files)
|
|
109
|
+
distribution = []
|
|
110
|
+
current_volume_files = []
|
|
111
|
+
current_volume_used = HEADER_OVERHEAD
|
|
112
|
+
|
|
113
|
+
files.each_with_index do |file, idx|
|
|
114
|
+
file_size = file[:compressed_size] + file[:header_size]
|
|
115
|
+
|
|
116
|
+
# Check if file fits in current volume
|
|
117
|
+
if (current_volume_used + file_size) <= @max_volume_size
|
|
118
|
+
current_volume_files << idx
|
|
119
|
+
current_volume_used += file_size
|
|
120
|
+
else
|
|
121
|
+
# Start new volume
|
|
122
|
+
distribution << current_volume_files unless current_volume_files.empty?
|
|
123
|
+
current_volume_files = [idx]
|
|
124
|
+
current_volume_used = HEADER_OVERHEAD + file_size
|
|
125
|
+
|
|
126
|
+
# Check if single file exceeds volume size
|
|
127
|
+
if current_volume_used > @max_volume_size
|
|
128
|
+
# File must span multiple volumes (not implemented yet)
|
|
129
|
+
# For now, just place it in its own volume
|
|
130
|
+
distribution << [idx]
|
|
131
|
+
current_volume_files = []
|
|
132
|
+
current_volume_used = HEADER_OVERHEAD
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Add final volume if not empty
|
|
138
|
+
distribution << current_volume_files unless current_volume_files.empty?
|
|
139
|
+
|
|
140
|
+
distribution
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Check if archive needs splitting
|
|
144
|
+
#
|
|
145
|
+
# @param total_size [Integer] Total archive size
|
|
146
|
+
# @return [Boolean] true if splitting needed
|
|
147
|
+
def self.needs_splitting?(total_size, max_volume_size)
|
|
148
|
+
total_size > max_volume_size
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Rar
|
|
6
|
+
module Rar5
|
|
7
|
+
module MultiVolume
|
|
8
|
+
# Volume writer for multi-volume archives
|
|
9
|
+
#
|
|
10
|
+
# This class writes individual .rar volume files with proper
|
|
11
|
+
# RAR5 headers and volume-specific flags.
|
|
12
|
+
#
|
|
13
|
+
# @example Write a volume
|
|
14
|
+
# writer = VolumeWriter.new('archive.part1.rar', volume_number: 1, is_last: false)
|
|
15
|
+
# writer.write_signature
|
|
16
|
+
# writer.write_main_header
|
|
17
|
+
# writer.write_file_data(file_header, compressed_data)
|
|
18
|
+
# writer.write_end_header
|
|
19
|
+
# writer.close
|
|
20
|
+
class VolumeWriter
|
|
21
|
+
# Main ARCHIVE flags (spec): 0x0001 volume,
|
|
22
|
+
# 0x0002 volume number field present
|
|
23
|
+
VOLUME_ARCHIVE_FLAG = MainHeader::ARCHIVE_FLAG_VOLUME
|
|
24
|
+
VOLUME_NUMBER_FLAG = MainHeader::ARCHIVE_FLAG_VOLUME_NUMBER
|
|
25
|
+
|
|
26
|
+
# End header flags
|
|
27
|
+
VOLUME_END_FLAG = 0x0001 # Not the last volume (more volumes follow)
|
|
28
|
+
|
|
29
|
+
# @return [String] Volume file path
|
|
30
|
+
attr_reader :path
|
|
31
|
+
|
|
32
|
+
# @return [Integer] Volume number (1-based)
|
|
33
|
+
attr_reader :volume_number
|
|
34
|
+
|
|
35
|
+
# @return [Boolean] Is this the last volume?
|
|
36
|
+
attr_reader :is_last
|
|
37
|
+
|
|
38
|
+
# @return [IO] File handle
|
|
39
|
+
attr_reader :io
|
|
40
|
+
|
|
41
|
+
# Initialize volume writer
|
|
42
|
+
#
|
|
43
|
+
# @param path [String] Output volume file path
|
|
44
|
+
# @param volume_number [Integer] Volume number (1-based)
|
|
45
|
+
# @param is_last [Boolean] Is this the last volume?
|
|
46
|
+
def initialize(path, volume_number:, is_last: false)
|
|
47
|
+
@path = path
|
|
48
|
+
@volume_number = volume_number
|
|
49
|
+
@is_last = is_last
|
|
50
|
+
@io = nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Open volume file for writing
|
|
54
|
+
#
|
|
55
|
+
# @return [void]
|
|
56
|
+
def open
|
|
57
|
+
@io = File.open(@path, "wb")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Close volume file
|
|
61
|
+
#
|
|
62
|
+
# @return [void]
|
|
63
|
+
def close
|
|
64
|
+
@io&.close
|
|
65
|
+
@io = nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Write with automatic open/close
|
|
69
|
+
#
|
|
70
|
+
# @yield [writer] Yields self for writing operations
|
|
71
|
+
# @return [void]
|
|
72
|
+
def write
|
|
73
|
+
open
|
|
74
|
+
yield self
|
|
75
|
+
ensure
|
|
76
|
+
close
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Write RAR5 signature
|
|
80
|
+
#
|
|
81
|
+
# @return [void]
|
|
82
|
+
def write_signature
|
|
83
|
+
raise Omnizip::IOError, "Volume not open" unless @io
|
|
84
|
+
|
|
85
|
+
# RAR5 signature: "Rar!\x1A\x07\x01\x00"
|
|
86
|
+
signature = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01,
|
|
87
|
+
0x00].pack("C*")
|
|
88
|
+
@io.write(signature)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Write Main header with volume flags
|
|
92
|
+
#
|
|
93
|
+
# @return [void]
|
|
94
|
+
def write_main_header
|
|
95
|
+
raise Omnizip::IOError, "Volume not open" unless @io
|
|
96
|
+
|
|
97
|
+
# Archive flags: every volume declares 0x0001; volumes
|
|
98
|
+
# after the first also carry the volume number field
|
|
99
|
+
# (spec: number is N-1, absent on the first volume).
|
|
100
|
+
archive_flags = VOLUME_ARCHIVE_FLAG
|
|
101
|
+
volume_number = nil
|
|
102
|
+
if @volume_number > 1
|
|
103
|
+
archive_flags |= VOLUME_NUMBER_FLAG
|
|
104
|
+
volume_number = @volume_number - 1
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
@io.write(MainHeader.new(archive_flags: archive_flags,
|
|
108
|
+
volume_number: volume_number).encode)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Write file data (header + compressed data)
|
|
112
|
+
#
|
|
113
|
+
# @param file_header [FileHeader] File header
|
|
114
|
+
# @param compressed_data [String] Compressed file data
|
|
115
|
+
# @return [void]
|
|
116
|
+
def write_file_data(file_header, compressed_data)
|
|
117
|
+
raise Omnizip::IOError, "Volume not open" unless @io
|
|
118
|
+
|
|
119
|
+
@io.write(file_header.encode)
|
|
120
|
+
@io.write(compressed_data)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Write End header with volume flags
|
|
124
|
+
#
|
|
125
|
+
# @return [void]
|
|
126
|
+
def write_end_header
|
|
127
|
+
raise Omnizip::IOError, "Volume not open" unless @io
|
|
128
|
+
|
|
129
|
+
# End-of-archive flags: 0x0001 marks a volume with more
|
|
130
|
+
# volumes following; the last volume ends with 0.
|
|
131
|
+
end_flags = @is_last ? 0 : VOLUME_END_FLAG
|
|
132
|
+
@io.write(EndHeader.new(end_flags: end_flags).encode)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Generate volume filename from base name
|
|
136
|
+
#
|
|
137
|
+
# @param base_path [String] Base archive path (e.g., "archive.rar")
|
|
138
|
+
# @param volume_number [Integer] Volume number (1-based)
|
|
139
|
+
# @param naming [String] Naming pattern ("part" or "volume")
|
|
140
|
+
# @return [String] Volume filename (e.g., "archive.part1.rar")
|
|
141
|
+
def self.volume_filename(base_path, volume_number, naming: "part")
|
|
142
|
+
dir = File.dirname(base_path)
|
|
143
|
+
basename = File.basename(base_path, ".*")
|
|
144
|
+
ext = File.extname(base_path)
|
|
145
|
+
|
|
146
|
+
volume_name = case naming
|
|
147
|
+
when "part"
|
|
148
|
+
"#{basename}.part#{volume_number}#{ext}"
|
|
149
|
+
when "volume"
|
|
150
|
+
"#{basename}.vol#{volume_number}#{ext}"
|
|
151
|
+
when "numeric"
|
|
152
|
+
# Simple numeric suffix: archive.rar, archive.r00, archive.r01, etc.
|
|
153
|
+
if volume_number == 1
|
|
154
|
+
"#{basename}#{ext}"
|
|
155
|
+
else
|
|
156
|
+
# Volume 2 => r00, Volume 3 => r01, etc.
|
|
157
|
+
"#{basename}.r#{format('%02d',
|
|
158
|
+
volume_number - 2)}"
|
|
159
|
+
end
|
|
160
|
+
else
|
|
161
|
+
"#{basename}.part#{volume_number}#{ext}"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Handle relative vs absolute paths
|
|
165
|
+
if dir == "."
|
|
166
|
+
volume_name
|
|
167
|
+
else
|
|
168
|
+
File.join(dir, volume_name)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Formats
|
|
7
|
+
module Rar
|
|
8
|
+
module Rar5
|
|
9
|
+
module Solid
|
|
10
|
+
# Solid LZMA encoder with persistent dictionary
|
|
11
|
+
#
|
|
12
|
+
# This encoder maintains LZMA dictionary state across multiple files,
|
|
13
|
+
# allowing later files to reference data from earlier files. This is
|
|
14
|
+
# the core technique behind solid compression's improved ratios.
|
|
15
|
+
#
|
|
16
|
+
# Unlike normal compression where each file is independent, solid
|
|
17
|
+
# compression treats all files as one continuous stream, maximizing
|
|
18
|
+
# dictionary-based compression efficiency.
|
|
19
|
+
#
|
|
20
|
+
# @example Compress multiple files in solid mode
|
|
21
|
+
# encoder = SolidEncoder.new(level: 5)
|
|
22
|
+
# compressed = encoder.compress_stream(concatenated_data)
|
|
23
|
+
class SolidEncoder
|
|
24
|
+
# @return [Integer] Compression level (1-5)
|
|
25
|
+
attr_reader :level
|
|
26
|
+
|
|
27
|
+
# @return [Hash] LZMA options
|
|
28
|
+
attr_reader :lzma_options
|
|
29
|
+
|
|
30
|
+
# Initialize solid encoder
|
|
31
|
+
#
|
|
32
|
+
# @param options [Hash] Encoder options
|
|
33
|
+
# @option options [Integer] :level Compression level (1-5, default: 3)
|
|
34
|
+
def initialize(options = {})
|
|
35
|
+
@level = options[:level] || 3
|
|
36
|
+
@lzma_options = build_lzma_options(@level)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Compress data with persistent dictionary
|
|
40
|
+
#
|
|
41
|
+
# @param data [String] Data to compress (concatenated files)
|
|
42
|
+
# @return [String] Compressed data
|
|
43
|
+
def compress_stream(data)
|
|
44
|
+
input = StringIO.new(data)
|
|
45
|
+
output = StringIO.new
|
|
46
|
+
output.set_encoding(Encoding::BINARY)
|
|
47
|
+
|
|
48
|
+
# Create LZMA encoder
|
|
49
|
+
lzma = Algorithms::LZMA.new
|
|
50
|
+
|
|
51
|
+
# Compress entire stream at once (maintains dictionary)
|
|
52
|
+
lzma.compress(input, output, @lzma_options)
|
|
53
|
+
|
|
54
|
+
output.string
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Decompress solid stream
|
|
58
|
+
#
|
|
59
|
+
# @param data [String] Compressed data
|
|
60
|
+
# @return [String] Decompressed data
|
|
61
|
+
def decompress_stream(data)
|
|
62
|
+
input = StringIO.new(data)
|
|
63
|
+
output = StringIO.new
|
|
64
|
+
output.set_encoding(Encoding::BINARY)
|
|
65
|
+
|
|
66
|
+
lzma = Algorithms::LZMA.new
|
|
67
|
+
lzma.decompress(input, output)
|
|
68
|
+
|
|
69
|
+
output.string
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Build LZMA options for solid compression
|
|
73
|
+
#
|
|
74
|
+
# Solid compression benefits from larger dictionaries since
|
|
75
|
+
# we're compressing more data at once.
|
|
76
|
+
#
|
|
77
|
+
# @param level [Integer] RAR5 compression level (1-5)
|
|
78
|
+
# @return [LzmaOptions] LZMA encoder options
|
|
79
|
+
def build_lzma_options(level)
|
|
80
|
+
# Use larger dictionaries for solid mode
|
|
81
|
+
# This allows better cross-file references
|
|
82
|
+
dict_size = 1 << case level
|
|
83
|
+
when 1 then 20 # 1 MB (fastest)
|
|
84
|
+
when 2 then 22 # 4 MB (fast)
|
|
85
|
+
when 3 then 24 # 16 MB (normal)
|
|
86
|
+
when 4 then 25 # 32 MB (good)
|
|
87
|
+
when 5 then 26 # 64 MB (best)
|
|
88
|
+
else 24 # default: 16 MB
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
LzmaOptions.new(level, dict_size)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Simple options class for LZMA parameters
|
|
95
|
+
class LzmaOptions
|
|
96
|
+
attr_reader :level, :dict_size
|
|
97
|
+
|
|
98
|
+
def initialize(level, dict_size)
|
|
99
|
+
@level = level
|
|
100
|
+
@dict_size = dict_size
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|