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,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Chunked
|
|
7
|
+
# Manage memory usage and enforce limits during chunked operations
|
|
8
|
+
class MemoryManager
|
|
9
|
+
DEFAULT_MAX_MEMORY = 256 * 1024 * 1024 # 256MB
|
|
10
|
+
|
|
11
|
+
attr_reader :current_usage, :max_memory
|
|
12
|
+
|
|
13
|
+
# Initialize memory manager
|
|
14
|
+
# @param options [Hash] Manager options
|
|
15
|
+
# @option options [Integer] :max Maximum memory in bytes
|
|
16
|
+
# @option options [String] :temp_dir Temporary directory for spill files
|
|
17
|
+
# @option options [Symbol] :strategy Spill strategy (:disk or :error)
|
|
18
|
+
def initialize(max: DEFAULT_MAX_MEMORY, temp_dir: nil, strategy: :disk)
|
|
19
|
+
@max_memory = max
|
|
20
|
+
@current_usage = 0
|
|
21
|
+
@buffers = {} # buffer => size mapping
|
|
22
|
+
@temp_files = []
|
|
23
|
+
@temp_dir = temp_dir
|
|
24
|
+
@strategy = strategy
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Allocate memory or spill to disk
|
|
28
|
+
# @param size [Integer] Size to allocate in bytes
|
|
29
|
+
# @return [String, Tempfile] Buffer or temp file
|
|
30
|
+
def allocate(size)
|
|
31
|
+
if can_allocate_in_memory?(size)
|
|
32
|
+
allocate_buffer(size)
|
|
33
|
+
else
|
|
34
|
+
handle_overflow(size)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Release allocated memory or temp file
|
|
39
|
+
# @param buffer [String, Tempfile] Buffer to release
|
|
40
|
+
# @return [Integer] Bytes released
|
|
41
|
+
def release(buffer)
|
|
42
|
+
case buffer
|
|
43
|
+
when String
|
|
44
|
+
release_buffer(buffer)
|
|
45
|
+
when Tempfile, File
|
|
46
|
+
release_temp_file(buffer)
|
|
47
|
+
else
|
|
48
|
+
0
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get available memory
|
|
53
|
+
# @return [Integer] Available memory in bytes
|
|
54
|
+
def available
|
|
55
|
+
[@max_memory - @current_usage, 0].max
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Check if over memory limit
|
|
59
|
+
# @return [Boolean] True if over limit
|
|
60
|
+
def over_limit?
|
|
61
|
+
@current_usage > @max_memory
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Get memory usage ratio
|
|
65
|
+
# @return [Float] Usage from 0.0 to 1.0
|
|
66
|
+
def usage_ratio
|
|
67
|
+
return 0.0 if @max_memory.zero?
|
|
68
|
+
|
|
69
|
+
@current_usage.to_f / @max_memory
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Cleanup all resources
|
|
73
|
+
# @return [self]
|
|
74
|
+
def cleanup
|
|
75
|
+
@buffers.clear
|
|
76
|
+
@temp_files.each do |tf|
|
|
77
|
+
begin
|
|
78
|
+
tf.close
|
|
79
|
+
rescue StandardError
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
begin
|
|
83
|
+
tf.unlink
|
|
84
|
+
rescue StandardError
|
|
85
|
+
nil
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
@temp_files.clear
|
|
89
|
+
@current_usage = 0
|
|
90
|
+
self
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Execute block with automatic cleanup
|
|
94
|
+
# @yield [manager] Block to execute with manager
|
|
95
|
+
# @return [Object] Block result
|
|
96
|
+
def self.with_manager(**options)
|
|
97
|
+
manager = new(**options)
|
|
98
|
+
begin
|
|
99
|
+
yield manager
|
|
100
|
+
ensure
|
|
101
|
+
manager.cleanup
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Spill buffer to disk
|
|
106
|
+
# @param buffer [String] Buffer data to spill
|
|
107
|
+
# @return [Tempfile] Temporary file
|
|
108
|
+
def spill_to_disk(buffer = nil)
|
|
109
|
+
tf = create_temp_file
|
|
110
|
+
tf.write(buffer) if buffer
|
|
111
|
+
tf.rewind
|
|
112
|
+
@temp_files << tf
|
|
113
|
+
tf
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
# Check if size can be allocated in memory
|
|
119
|
+
def can_allocate_in_memory?(size)
|
|
120
|
+
@current_usage + size <= @max_memory
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Allocate in-memory buffer
|
|
124
|
+
def allocate_buffer(size)
|
|
125
|
+
buffer = String.new(capacity: size, encoding: Encoding::BINARY)
|
|
126
|
+
@buffers[buffer] = size
|
|
127
|
+
@current_usage += size
|
|
128
|
+
buffer
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Release in-memory buffer
|
|
132
|
+
def release_buffer(buffer)
|
|
133
|
+
size = @buffers.delete(buffer)
|
|
134
|
+
if size
|
|
135
|
+
@current_usage -= size
|
|
136
|
+
size
|
|
137
|
+
else
|
|
138
|
+
0
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Release temp file
|
|
143
|
+
def release_temp_file(file)
|
|
144
|
+
if @temp_files.delete(file)
|
|
145
|
+
size = begin
|
|
146
|
+
file.size
|
|
147
|
+
rescue StandardError
|
|
148
|
+
0
|
|
149
|
+
end
|
|
150
|
+
begin
|
|
151
|
+
file.close
|
|
152
|
+
rescue StandardError
|
|
153
|
+
nil
|
|
154
|
+
end
|
|
155
|
+
begin
|
|
156
|
+
file.unlink
|
|
157
|
+
rescue StandardError
|
|
158
|
+
nil
|
|
159
|
+
end
|
|
160
|
+
size
|
|
161
|
+
else
|
|
162
|
+
0
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Handle memory overflow based on strategy
|
|
167
|
+
def handle_overflow(size)
|
|
168
|
+
case @strategy
|
|
169
|
+
when :disk
|
|
170
|
+
spill_to_disk
|
|
171
|
+
when :error
|
|
172
|
+
message = "Memory limit exceeded: " \
|
|
173
|
+
"#{@current_usage + size} > #{@max_memory}"
|
|
174
|
+
raise MemoryError, message
|
|
175
|
+
else
|
|
176
|
+
raise ArgumentError, "Unknown spill strategy: #{@strategy}"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Create temporary file
|
|
181
|
+
def create_temp_file
|
|
182
|
+
Tempfile.new(
|
|
183
|
+
["omnizip_chunk_", ".tmp"],
|
|
184
|
+
@temp_dir,
|
|
185
|
+
binmode: true,
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Error raised when memory limit is exceeded
|
|
191
|
+
class MemoryError < Omnizip::Error
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Chunked
|
|
5
|
+
# Read large files in chunks for memory-efficient processing
|
|
6
|
+
class Reader
|
|
7
|
+
DEFAULT_CHUNK_SIZE = 64 * 1024 * 1024 # 64MB
|
|
8
|
+
|
|
9
|
+
attr_reader :file_path, :chunk_size, :total_size
|
|
10
|
+
|
|
11
|
+
# Initialize a chunked reader
|
|
12
|
+
# @param file_path [String] Path to file to read
|
|
13
|
+
# @param options [Hash] Reader options
|
|
14
|
+
# @option options [Integer] :chunk_size Chunk size in bytes
|
|
15
|
+
def initialize(file_path, chunk_size: DEFAULT_CHUNK_SIZE)
|
|
16
|
+
@file_path = file_path
|
|
17
|
+
@chunk_size = chunk_size
|
|
18
|
+
@total_size = File.size(file_path)
|
|
19
|
+
@position = 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Read next chunk from file
|
|
23
|
+
# @return [String, nil] Chunk data or nil if EOF
|
|
24
|
+
def read_chunk
|
|
25
|
+
return nil if @position >= @total_size
|
|
26
|
+
|
|
27
|
+
File.open(@file_path, "rb") do |f|
|
|
28
|
+
f.seek(@position)
|
|
29
|
+
chunk = f.read(@chunk_size)
|
|
30
|
+
@position += chunk.bytesize if chunk
|
|
31
|
+
chunk
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Iterate through all chunks
|
|
36
|
+
# @yield [chunk, position, total] Block called for each chunk
|
|
37
|
+
def each_chunk
|
|
38
|
+
reset
|
|
39
|
+
while (chunk = read_chunk)
|
|
40
|
+
yield chunk, @position - chunk.bytesize, @total_size
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Get current progress as percentage
|
|
45
|
+
# @return [Float] Progress from 0.0 to 1.0
|
|
46
|
+
def progress
|
|
47
|
+
return 1.0 if @total_size.zero?
|
|
48
|
+
|
|
49
|
+
@position.to_f / @total_size
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Reset reader to beginning of file
|
|
53
|
+
# @return [self]
|
|
54
|
+
def reset
|
|
55
|
+
@position = 0
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Check if at end of file
|
|
60
|
+
# @return [Boolean] True if at EOF
|
|
61
|
+
def eof?
|
|
62
|
+
@position >= @total_size
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Get number of chunks needed for file
|
|
66
|
+
# @return [Integer] Number of chunks
|
|
67
|
+
def chunk_count
|
|
68
|
+
(@total_size.to_f / @chunk_size).ceil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Get remaining bytes to read
|
|
72
|
+
# @return [Integer] Remaining bytes
|
|
73
|
+
def remaining
|
|
74
|
+
@total_size - @position
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Chunked
|
|
5
|
+
# Write large files incrementally in chunks
|
|
6
|
+
class Writer
|
|
7
|
+
DEFAULT_CHUNK_SIZE = 64 * 1024 * 1024 # 64MB
|
|
8
|
+
FLUSH_THRESHOLD = 10 # Flush every N chunks
|
|
9
|
+
|
|
10
|
+
attr_reader :output_path, :chunk_size, :written
|
|
11
|
+
|
|
12
|
+
# Initialize a chunked writer
|
|
13
|
+
# @param output_path [String] Path to output file
|
|
14
|
+
# @param options [Hash] Writer options
|
|
15
|
+
# @option options [Integer] :chunk_size Chunk size in bytes
|
|
16
|
+
def initialize(output_path, chunk_size: DEFAULT_CHUNK_SIZE)
|
|
17
|
+
@output_path = output_path
|
|
18
|
+
@chunk_size = chunk_size
|
|
19
|
+
@written = 0
|
|
20
|
+
@chunks_written = 0
|
|
21
|
+
@file_handle = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Write a chunk to file
|
|
25
|
+
# @param chunk [String] Data to write
|
|
26
|
+
# @return [Integer] Bytes written
|
|
27
|
+
def write_chunk(chunk)
|
|
28
|
+
ensure_file_open
|
|
29
|
+
|
|
30
|
+
bytes = @file_handle.write(chunk)
|
|
31
|
+
@written += bytes
|
|
32
|
+
@chunks_written += 1
|
|
33
|
+
|
|
34
|
+
# Flush periodically to disk
|
|
35
|
+
flush if (@chunks_written % FLUSH_THRESHOLD).zero?
|
|
36
|
+
|
|
37
|
+
bytes
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Write data from a source in chunks
|
|
41
|
+
# @param source [String, IO] Source to read from
|
|
42
|
+
# @return [Integer] Total bytes written
|
|
43
|
+
def write_from(source)
|
|
44
|
+
case source
|
|
45
|
+
when String
|
|
46
|
+
# File path
|
|
47
|
+
if File.exist?(source)
|
|
48
|
+
Reader.new(source, chunk_size: @chunk_size).each_chunk do |chunk|
|
|
49
|
+
write_chunk(chunk)
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
# Treat as data
|
|
53
|
+
write_chunk(source)
|
|
54
|
+
end
|
|
55
|
+
when IO, StringIO
|
|
56
|
+
# IO object
|
|
57
|
+
while (chunk = source.read(@chunk_size))
|
|
58
|
+
break if chunk.empty?
|
|
59
|
+
|
|
60
|
+
write_chunk(chunk)
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
raise ArgumentError, "Unsupported source type: #{source.class}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@written
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Flush buffered data to disk
|
|
70
|
+
# @return [self]
|
|
71
|
+
def flush
|
|
72
|
+
@file_handle&.flush
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Close the file handle
|
|
77
|
+
# @return [self]
|
|
78
|
+
def close
|
|
79
|
+
if @file_handle
|
|
80
|
+
flush
|
|
81
|
+
@file_handle.close
|
|
82
|
+
@file_handle = nil
|
|
83
|
+
end
|
|
84
|
+
self
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Finalize the file (alias for close)
|
|
88
|
+
# @return [self]
|
|
89
|
+
def finish
|
|
90
|
+
close
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Execute block with writer, auto-close
|
|
94
|
+
# @yield [writer] Block to write data
|
|
95
|
+
# @return [Integer] Total bytes written
|
|
96
|
+
def self.with_writer(output_path, **options)
|
|
97
|
+
writer = new(output_path, **options)
|
|
98
|
+
begin
|
|
99
|
+
yield writer
|
|
100
|
+
writer.written
|
|
101
|
+
ensure
|
|
102
|
+
writer.close
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
# Ensure file handle is open
|
|
109
|
+
def ensure_file_open
|
|
110
|
+
return if @file_handle
|
|
111
|
+
|
|
112
|
+
# Create parent directory if needed
|
|
113
|
+
dir = File.dirname(@output_path)
|
|
114
|
+
FileUtils.mkdir_p(dir)
|
|
115
|
+
|
|
116
|
+
@file_handle = File.open(@output_path, "wb")
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
# Chunked processing for memory-efficient large file handling
|
|
5
|
+
module Chunked
|
|
6
|
+
# Configuration for chunked operations
|
|
7
|
+
class Configuration
|
|
8
|
+
attr_accessor :chunk_size, :max_memory, :temp_directory, :spill_strategy
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@chunk_size = 64 * 1024 * 1024 # 64MB
|
|
12
|
+
@max_memory = 256 * 1024 * 1024 # 256MB
|
|
13
|
+
@temp_directory = nil # Use system default
|
|
14
|
+
@spill_strategy = :disk # or :error
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Global configuration
|
|
20
|
+
def configuration
|
|
21
|
+
@configuration ||= Configuration.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Configure chunked operations
|
|
25
|
+
# @yield [config] Configuration block
|
|
26
|
+
def configure
|
|
27
|
+
yield configuration
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Compress file with chunked processing
|
|
31
|
+
# @param input [String] Input file path
|
|
32
|
+
# @param output [String] Output file path
|
|
33
|
+
# @param options [Hash] Compression options
|
|
34
|
+
# @option options [Integer] :chunk_size Chunk size in bytes
|
|
35
|
+
# @option options [Integer] :max_memory Maximum memory usage in bytes
|
|
36
|
+
# @option options [Symbol] :compression Compression method
|
|
37
|
+
# @option options [Proc] :progress Progress callback
|
|
38
|
+
# @return [String] Output file path
|
|
39
|
+
def compress_file(input, output, **options)
|
|
40
|
+
chunk_size = options[:chunk_size] || configuration.chunk_size
|
|
41
|
+
options[:max_memory] || configuration.max_memory
|
|
42
|
+
progress = options[:progress]
|
|
43
|
+
|
|
44
|
+
unless File.exist?(input)
|
|
45
|
+
raise Errno::ENOENT,
|
|
46
|
+
"Input file not found: #{input}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
reader = Reader.new(input, chunk_size: chunk_size)
|
|
50
|
+
total_size = reader.total_size
|
|
51
|
+
processed = 0
|
|
52
|
+
|
|
53
|
+
Omnizip::Zip::File.create(output) do |zip|
|
|
54
|
+
basename = File.basename(input)
|
|
55
|
+
|
|
56
|
+
# Use add with a block instead of private add_data
|
|
57
|
+
zip.add(basename) do
|
|
58
|
+
data = String.new(encoding: Encoding::BINARY)
|
|
59
|
+
|
|
60
|
+
reader.each_chunk do |chunk|
|
|
61
|
+
data << chunk
|
|
62
|
+
processed += chunk.bytesize
|
|
63
|
+
|
|
64
|
+
if progress
|
|
65
|
+
percentage = (processed.to_f / total_size * 100).round(2)
|
|
66
|
+
progress.call(processed, total_size, percentage)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
data
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
output
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Decompress file with chunked processing
|
|
78
|
+
# @param input [String] Input archive path
|
|
79
|
+
# @param output [String] Output file path
|
|
80
|
+
# @param options [Hash] Decompression options
|
|
81
|
+
# @option options [Integer] :chunk_size Chunk size in bytes
|
|
82
|
+
# @option options [Proc] :progress Progress callback
|
|
83
|
+
# @return [String] Output file path
|
|
84
|
+
def decompress_file(input, output, **options)
|
|
85
|
+
chunk_size = options[:chunk_size] || configuration.chunk_size
|
|
86
|
+
progress = options[:progress]
|
|
87
|
+
|
|
88
|
+
unless File.exist?(input)
|
|
89
|
+
raise Errno::ENOENT,
|
|
90
|
+
"Input archive not found: #{input}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
writer = Writer.new(output, chunk_size: chunk_size)
|
|
94
|
+
processed = 0
|
|
95
|
+
|
|
96
|
+
Omnizip::Zip::File.open(input) do |zip|
|
|
97
|
+
entry = zip.entries.first
|
|
98
|
+
total_size = entry.size
|
|
99
|
+
|
|
100
|
+
# Read the full entry content
|
|
101
|
+
content = zip.get_input_stream(entry)
|
|
102
|
+
|
|
103
|
+
# Write in chunks
|
|
104
|
+
offset = 0
|
|
105
|
+
while offset < content.bytesize
|
|
106
|
+
chunk = content.byteslice(offset, chunk_size) || ""
|
|
107
|
+
break if chunk.empty?
|
|
108
|
+
|
|
109
|
+
writer.write_chunk(chunk)
|
|
110
|
+
processed += chunk.bytesize
|
|
111
|
+
offset += chunk.bytesize
|
|
112
|
+
|
|
113
|
+
if progress
|
|
114
|
+
percentage = (processed.to_f / total_size * 100).round(2)
|
|
115
|
+
progress.call(processed, total_size, percentage)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
writer.close
|
|
121
|
+
output
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
require_relative "chunked/reader"
|
|
128
|
+
require_relative "chunked/writer"
|
|
129
|
+
require_relative "chunked/memory_manager"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2024 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
# This file is part of Omnizip.
|
|
7
|
+
#
|
|
8
|
+
# Omnizip is a pure Ruby port of 7-Zip compression algorithms.
|
|
9
|
+
# Based on the 7-Zip LZMA SDK by Igor Pavlov.
|
|
10
|
+
#
|
|
11
|
+
# This library is free software; you can redistribute it and/or
|
|
12
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
13
|
+
# License as published by the Free Software Foundation; either
|
|
14
|
+
# version 2.1 of the License, or (at your option) any later version.
|
|
15
|
+
#
|
|
16
|
+
# See the COPYING file for the complete text of the license.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
module Omnizip
|
|
20
|
+
# Output formatting utilities for CLI commands.
|
|
21
|
+
#
|
|
22
|
+
# Provides methods for formatting compression statistics, error messages,
|
|
23
|
+
# and other CLI output in a user-friendly manner.
|
|
24
|
+
module CliOutputFormatter
|
|
25
|
+
class << self
|
|
26
|
+
# Format compression statistics for display.
|
|
27
|
+
#
|
|
28
|
+
# @param input_size [Integer] Original size in bytes
|
|
29
|
+
# @param output_size [Integer] Compressed size in bytes
|
|
30
|
+
# @param elapsed_time [Float] Time taken in seconds
|
|
31
|
+
# @return [String] Formatted statistics
|
|
32
|
+
def format_compression_stats(input_size, output_size, elapsed_time)
|
|
33
|
+
ratio = if input_size.positive?
|
|
34
|
+
(output_size.to_f / input_size * 100).round(2)
|
|
35
|
+
else
|
|
36
|
+
0.0
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
[
|
|
40
|
+
"Input size: #{format_size(input_size)}",
|
|
41
|
+
"Output size: #{format_size(output_size)}",
|
|
42
|
+
"Ratio: #{ratio}%",
|
|
43
|
+
"Time: #{elapsed_time.round(3)}s",
|
|
44
|
+
].join("\n")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Format file size in human-readable format.
|
|
48
|
+
#
|
|
49
|
+
# @param bytes [Integer] Size in bytes
|
|
50
|
+
# @return [String] Formatted size
|
|
51
|
+
def format_size(bytes)
|
|
52
|
+
units = %w[B KB MB GB TB]
|
|
53
|
+
return "0 B" if bytes.zero?
|
|
54
|
+
|
|
55
|
+
exp = (Math.log(bytes) / Math.log(1024)).floor
|
|
56
|
+
exp = [exp, units.length - 1].min
|
|
57
|
+
|
|
58
|
+
size = (bytes.to_f / (1024**exp)).round(2)
|
|
59
|
+
"#{size} #{units[exp]}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Format error message for display.
|
|
63
|
+
#
|
|
64
|
+
# @param error [Exception] The error object
|
|
65
|
+
# @return [String] Formatted error message
|
|
66
|
+
def format_error(error)
|
|
67
|
+
"Error: #{error.message}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Format algorithm information as a table.
|
|
71
|
+
#
|
|
72
|
+
# @param algorithms [Array<Models::AlgorithmMetadata>] Algorithms
|
|
73
|
+
# @return [String] Formatted table
|
|
74
|
+
def format_algorithms_table(algorithms)
|
|
75
|
+
return "No algorithms registered." if algorithms.empty?
|
|
76
|
+
|
|
77
|
+
lines = []
|
|
78
|
+
lines << "Available compression algorithms:"
|
|
79
|
+
lines << ""
|
|
80
|
+
|
|
81
|
+
max_name = algorithms.map { |a| a.name.to_s.length }.max
|
|
82
|
+
max_desc = algorithms.map { |a| a.description.length }.max
|
|
83
|
+
|
|
84
|
+
algorithms.each do |algo|
|
|
85
|
+
name = algo.name.to_s.ljust(max_name)
|
|
86
|
+
desc = algo.description.ljust(max_desc)
|
|
87
|
+
version = "v#{algo.version}"
|
|
88
|
+
lines << " #{name} - #{desc} (#{version})"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
lines.join("\n")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Print verbose message if verbose mode is enabled.
|
|
95
|
+
#
|
|
96
|
+
# @param message [String] The message to print
|
|
97
|
+
# @param verbose [Boolean] Whether verbose mode is enabled
|
|
98
|
+
# @return [void]
|
|
99
|
+
def verbose_puts(message, verbose)
|
|
100
|
+
puts message if verbose
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|